File: README.md

package info (click to toggle)
erlang 1%3A27.3.4.1%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 225,000 kB
  • sloc: erlang: 1,658,966; ansic: 405,769; cpp: 177,850; xml: 82,435; makefile: 15,031; sh: 14,401; lisp: 9,812; java: 8,603; asm: 6,541; perl: 5,836; python: 5,484; sed: 72
file content (645 lines) | stat: -rw-r--r-- 22,351 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
Yielding C Fun
==============

Introduction
------------

Yielding C Fun (YCF) is a tool that transforms functions written in a
subset of the C programming language so that they become yieldable. A
yieldable function can be suspended/yielded/paused/trapped (either
automatically or where the user has inserted a particular statement)
and then be resumed at a later point. Yileldable functions are also
called [coroutines](https://en.wikipedia.org/wiki/Coroutine).

Difference Between Yielding C Fun and Coroutine Libraries
---------------------------------------------------------

Several libraries implement [coroutine support for the C programming
language](https://en.wikipedia.org/wiki/Coroutine#Implementations_for_C)
(e.g., \[[1], [2], [3], [4], [5], [6], [7], [8], [9], [10], [11],
[12], [13]\]). These libraries either rely on platform-specific code
or do not save call stack variables. Yielding C Fun (YCF) does not
have any of these two limitations. YCF can accomplish this as it is a
source-to-source transformation tool and not only a library.

YCF has been created to make it easier to implement yielding Erlang
[NIFs](http://erlang.org/doc/tutorial/nif.html) and
[BIFs](http://erlang.org/pipermail/erlang-questions/2009-October/046899.html)
(i.e., Erlang functions that are written in C). Below are examples of
YCF features that are useful when implementing yielding Erlang NIFs
and BIFs:

 * YCF automatically generates a destroy function for each yieldable
   function. The destroy function frees resources that are used by a
   suspended function. The destroy function is useful when a suspended
   function needs to abort (e.g., when the Erlang process that invoked
   the function has died).

 * YCF can automatically insert code that yields functions after a
   user specifiable number of loop iterations and goto statements.

 * YCF has a hook system that lets the user insert code that is
   triggered when certain events happen (e.g., when a function
   yields).

The main limitations of YCF are that it cannot handle all valid C code
and that it cannot make library functions without source code
yieldable. Pointers to stack-allocated data are also not allowed (YCF
has a memory allocation function called `YCF_STACK_ALLOC` to work
around this issue).

Requirements
------------

* A C99 compatible C compiler
* make (optional but useful for building)

Compile and Test
----------------

Build the executable `$YCF_ROOT/bin/yielding_c_fun.bin`:

    make

Build the executable and run all tests:

    make test

Getting Started
---------------

A brief introduction tutorial can be found
[here](doc/thread_tutorial.md). This tutorial is a perfect place to
start!

The "[test/examples/](test/examples/)" folder in this repository
contains many small examples that are useful when learning about
YCF. YCF's automatic tests use these examples as well. The driver for
these tests is located in `test/test.sh`.

[This Erlang NIF example](test/examples/sha256_erlang_nif/) shows how
one can use YCF to write a yielding Erlang NIF library.


Command Line Parameters
-----------------------

```
Usage: yielding_c_fun [-h]
       yielding_c_fun [-use_gc [-print_gc_info]]
                      [-log_max_mem_usage log_file]
                      [(( -f | -frec | -fnoauto ) function_name)...
                       [-output_file_name output_file]
                       [-header_file_name header_file]
                       [-debug]
                       [-only_yielding_funs]
                       [-static_aux_funs]
                       input_c_file]]
```

* `-h`

  Print help text

* `-use_gc`

  Use garbage collection. The garbage collection system assumes that
  the C call stack consists of a continuous memory block and is
  therefore not enabled by default even though this assumption is
  valid on all major platforms. YCF does not reclaim any allocated
  memory if the `-use_gc` flag is not set.

* `-print_gc_info`

  (For debugging) Print garbage collection information to `stderr`

* `-log_max_mem_usage log_file`

  (For debugging) Print the peak memory usage of the tool to the file
  `log_file`

* `-fnoauto function_name`

  Generate a yieldable version of the function named
  function_name. The user can use `YCF_YIELD()`,
  `YCF_YIELD_NO_REDS()`, and `YCF_CONSUME_REDS(N)` to control
  when and where the function should yield. See the section titled
  "Special Statements and Macros" for more information.

* `-f function_name`

  Generate a yieldable version of the function named
  `function_name`. The generated function automatically decrements the
  reduction counter by one at the beginning of loop bodies and before
  goto statements. The function yields automatically if the reduction
  counter reaches a value that is zero or smaller after it has been
  decremented.

* `-frec function_name`

  Same as the `-f` option with the exception that the generated function
  also decrements one reduction before calls to other yieldable
  functions and before returning. The function yields automatically if
  the reduction counter reaches a value that is zero or smaller after
  it has been decremented.

* `-fexternal function_name`

  YCF expects that a yielding version of the function called
  `function_name` is generated externally. Calls to the function
  called `function_name` from yielding functions calls the externally
  generated yielding version of the function called `function_name`.

* `-output_file_name output_file`

  Output the generated code to a file named output_file. The output
  is printed to standard output if this parameter is unspecified.

* `-header_file_name header_file`

  Generate a header file containing only declarations for the generated
  functions and write the header file to the file named header_file.

* `-debug`

  Generate debug code that executes when a function yields. The debug
  code searches the call stack of the yielding functions for pointers
  to data that is allocated on the call stack. The program crashes
  with an error message if any such pointer is found.

  The generated debug code depends on that a function called
  `ycf_debug_get_stack_start()` is declared somewhere in the
  program. The `ycf_debug_get_stack_start()` functions should return a
  value of type `void*`. Example:
  
          static _Thread_local void* ycf_debug_global_stack_start_ptr = NULL;
          void* ycf_debug_get_stack_start() {
              return ycf_debug_global_stack_start_ptr;
          }
  
  If `ycf_debug_get_stack_start()` returns `NULL`, the value of the
  `ycf_yield_state` parameter will be used as the start of the stack (it
  is assumed that the stack grows towards lower addresses). If
  `ycf_debug_get_stack_start()` returns something different than `NULL`,
  that value will be used as the start of the stack. To check that
  nested yielding functions do not have pointers to the call stack,
  one have to make sure that `ycf_debug_get_stack_start()` returns
  something different than `NULL` (otherwise, each function will just
  check for pointers to its own frame). Example:
  
          ycf_debug_global_stack_start_ptr = &wb;
          ret = fun_ycf_gen_yielding(&nr_of_reductions,&wb,NULL,allocator,freer,NULL,0,NULL,1);
          ycf_debug_global_stack_start_ptr = NULL;

* `-only_yielding_funs`

  Print only the generated functions and struct declarations. The
  default behavior is to insert the generated functions into a copy of
  the input source file.

* `-static_aux_funs`

  Make the generated auxiliary functions static (i.e., local to the C
  compilation unit)

* `input_c_file`

  The source file containing the functions that YCF shall create
  yieldable versions of. YCF does not do any macro expansions. There
  are several restrictions on the code that YCF can handle that are
  described in the "Code Restrictions" section below.

Generated Functions
-------------------

YCF generates three functions for each function name that it is
given. These functions have the original function name as prefix and
different suffixes. Descriptions of the functions that YCF generates
follows below:


```c

/* Shall return a pointer to a memory block of size size bytes. */
typedef void* (*ycf_yield_alloc_type) (size_t size ,void* ctx);
/* Shall free the memory block which block points to. */
typedef void (*ycf_yield_free_type) (void* block,void* ctx);

return_type_of_orginal_fun
original_fun_name_ycf_gen_yielding(
               long * ycf_nr_of_reductions,
               void ** ycf_yield_state,
               void * ycf_extra_context,
               ycf_yield_alloc_type ycf_yield_alloc,
               ycf_yield_free_type ycf_yield_free,
               void * ycf_yield_alloc_free_context,
               size_t ycf_stack_alloc_size_or_max_size,
               void* ycf_stack_alloc_data
               paremeters_of_orginal_function);
```

The generated function with suffix `_ycf_gen_yielding` initiates the
call of a yieldable function. Its parameters and return types are
described below:

* `return_type_of_orginal_fun`

  The return type is the same as the return type of the original
  function. The return value is the return value of the function if
  the `_ycf_gen_yielding` function returns without yielding and is
  uninitialized otherwise.

* `long * ycf_nr_of_reductions`

  (input/output parameter) Gives the yieldable function the number of
  reductions it can consume before yielding and is also used to write
  back the number of reductions that are left when the function
  returns or yields.

* `void ** ycf_yield_state`

  (input/output parameter) Should be a pointer to a pointer to NULL
  when the `_ycf_gen_yielding` function is called. The value pointed
  to by ycf_yield_state is NULL when the `_ycf_gen_yielding` function
  has returned if it did not yield and points to the yield state
  otherwise.

* `void * ycf_extra_context`

  This parameter is useful if the yieldable function needs to access
  data that may change when it resumes after having been yielded. The
  extra context can be accessed from within the yieldable function
  with the `YCF_GET_EXTRA_CONTEXT()` function.

* `ycf_yield_alloc_type ycf_yield_alloc`

  A memory allocator function that is used by the yieldable function
  to allocate memory (e.g., to save the state when the function
  yields).

* `ycf_yield_free ycf_yield_free`

  A memory free function that should free a memory block that has been
  allocated with ycf_yield_alloc.

* `void * ycf_yield_alloc_free_context`

  A context that is passed as the second argument to `ycf_yield_alloc`
  and `ycf_yield_free`.

* `size_t ycf_stack_alloc_size_or_max_size`

  The max number of total bytes that can be allocated with the special
  allocator `YCF_STACK_ALLOC(n)`. This can be set to 0 if
  `YCF_STACK_ALLOC(n)` is unused. See the documentation of
  `YCF_STACK_ALLOC(n)` below for more information.

* `void* ycf_stack_alloc_data`

  A pointer to a data block that will be used by
  `YCF_STACK_ALLOC(n)`. The value of `ycf_stack_alloc_data` should be
  `NULL` or a pointer to a data block that is least
  `ycf_stack_alloc_size_or_max_size` bytes large if
  `YCF_STACK_ALLOC(n)` is used within the yieldable function or any
  yieldable function that is called by the yieldable function. The
  `ycf_yield_alloc` and `ycf_yield_free` functions will be used to
  automatically alloc and free a data block when needed, if
  `ycf_stack_alloc_data` is set to `NULL`. The value of
  `ycf_stack_alloc_data` does not matter if `YCF_STACK_ALLOC(n)` is
  unused.

* `paremeters_of_orginal_function`

  Parameters that the original function takes will be placed in the
  end of the parameter list of the `ycf_gen_yielding` function.


```c
return_type_of_orginal_fun
original_fun_name_ycf_gen_continue(
                     long * ycf_nr_of_reduction,
                     void ** ycf_yield_state,
                     void * ycf_extra_context);
```

The generated function with the suffix `_ycf_gen_continue` is used to
resume a yielded function. The descriptions of the parameters and
return type for the `_ycf_gen_yielding` function above are valid for
the `_ycf_gen_continue` function as well, with the exception that the
parameter `ycf_yield_state` should point to a pointer to a yield state
(created in the previous call to `_ycf_gen_yielding` or
`_ycf_gen_continue`).

```c
void original_fun_name_ycf_gen_destroy(void * ycf_yield_state);
```

The `_gen_destroy` function frees the state of a yieldable function
that has been suspended. This function should only be called when one
wants to cancel a yielded call before completion. Notice that the
parameter `ycf_yield_state` points directly to the yield state, unlike
the parameter of the `_ycf_gen_yielding` and `_ycf_gen_continue`
functions with the same name. The `_gen_destroy` function
automatically calls the destroy function for active subcalls to
yieldable functions.



The `YCF_YIELD_CODE_GENERATED` Macro
------------------------------------

YCF also generates code that defines the macro
`YCF_YIELD_CODE_GENERATED`. This macro may be useful if one wants to
compile one version of a program with yieldable functions and another
without yieldable functions.

Special Statements and Macros
-----------------------------

Some special statements and macros can be used from within a yieldable
function. Descriptions of those follow below:

* `YCF_YIELD();`

  The `YCF_YIELD();` statement sets the reduction counter to zero
  and yields the function when it is executed.

* `YCF_YIELD_NO_REDS();`

  The `YCF_YIELD_NO_REDS();` statement yields the function
  without changing the reduction counter when it is executed.

* `YCF_CONSUME_REDS(N);`

  The `YCF_CONSUME_REDS(N);` statement decrements the
  reductions counter by N and yields if the reduction counter is less
  than or equal to zero after the decrement.

* `YCF_STACK_ALLOC(N)`

  The `YCF_STACK_ALLOC(N)` macro uses an allocator that is included in
  the code generated by YCF to allocate a block with `N +
  (sizeof(void * ) - (N % sizeof(void*)))` bytes and return a pointer
  to these bytes. A block that has been allocated with
  `YCF_STACK_ALLOC(N)` is automatically freed when the function that
  allocated the block returns. Memory blocks that are allocated with
  `YCF_STACK_ALLOC(N)` do not move when a yieldable function yields
  and then resumes again. In contrast, data that is allocated directly
  on the call stack may move when a function yields and
  resumes. `YCF_STACK_ALLOC(N)` can thus be useful if one wants to
  port C code that has variables that point to data that is allocated
  on the call stack. The parameters `ycf_stack_alloc_size_or_max_size`
  and `ycf_stack_alloc_data` of the `_ycf_gen_yielding` function need
  to be set correctly if `YCF_STACK_ALLOC(N)` is used. Please see the
  description of the `_ycf_gen_yielding` function in the "Generated
  Functions" section above for details about those parameters. Notice
  also that the `-debug` flag that is described in the "Command Line
  Parameters" section above can be useful when one wants to find out
  if a function points to data that is allocated on the call stack of
  a yieldable function.

* `YCF_GET_EXTRA_CONTEXT()`

  The `YCF_GET_EXTRA_CONTEXT()` macro returns the value of the
  `ycf_extra_context` parameter that was passed to the latest call of
  one of the corresponding `_ycf_gen_yielding` or `_ycf_gen_continue`
  functions. See the "Generated Functions" section above for
  information about the parameters of `_ycf_gen_yielding` and
  `_ycf_gen_continue` functions.

* `YCF_NR_OF_REDS_LEFT()`

  The `YCF_NR_OF_REDS_LEFT()` macro returns the current value of
  the reduction counter (a value of type `long`).

* `YCF_SET_NR_OF_REDS_LEFT(NEW_NR_OF_REDS_LEFT)`

  The `YCF_SET_NR_OF_REDS_LEFT(NEW_NR_OF_REDS_LEFT)` macro sets
  the value that the reduction counter (which stores a value of type
  `long`) to `NEW_NR_OF_REDS_LEFT`.

* `YCF_MAX_NR_OF_REDS`

  The `YCF_MAX_NR_OF_REDS` macro returns the maximum value that the
  reduction counter may have.

Code Restrictions
-----------------

YCF cannot parse all valid C code. The code restrictions that
yieldable functions need to follow are described below. It is
recommended to check that the generated code is correct.

* **Declarations**

  Variable declarations and parameters of yieldable functions need to
  be in the following form:

  ```
  "(optional) type descriptor (i.e., struct, union or enum)"
  
  "type name"

  "(optional) one or more star characters (i.e., *)"

  "variable name"
  
  "(optional) one or more square brackets with a number inside (e.g, [3])"
  
  "(optional) one or more empty square brackets (e.g, [])"
  
  "(optional) equal sign followed by an expression (automatic array
  initialization and struct initialization of the form
  {.filed_name=value...} are not allowed)"

  "semicolon"
  ```
  
  Here are some examples of declarations that are **correct**:
  
  ```c
  int var1;
  int var2 = 1;
  int var3 = var2 + 1;
  int var4 = function(var3);
  int * var5 = malloc(sizeof(int*));
  int ** var6 = malloc(sizeof(int*)*10);
  int ***** var7;
  struct struct_name var8;
  struct struct_name var9 = function2();
  double var10[128];
  double var11[128][];
  double * var12[128];
  ```
  
  Here are examples of declarations that are **incorrect**:
  
  ```c
  int var1, var2;
  int var1 = 1, var2 = 10;
  void (*printer_t)(int);
  ```
  
  Note that one has to use a `typedef` to be able to declare a
  function pointer variable.

* **Pointers**

  Pointers to call-stack-allocated data are not allowed. The
  `YCF_YIELD_ALLOC(N)` function, which is described in the "Special
  Statements and Macros" section above, can be used to work around
  this limitation. The `-debug` flag that is described in the "Command
  Line Parameters" section above, can be useful when one wants to find
  out if a yieldable function points to call-stack-allocated data.


* **Macros**

  YCF does not expand macros so macros in functions that YCF
  transforms should not "hide" variables or any other code that is
  relevant for yielding.

* **Calls to a Yieldable Function from Another Yieldable Function**

  Calls to a yieldable function from another yieldable function need
  to be in a form that YCF recognizes. Such calls need to be in one of
  the following forms:

  * As a separate statement:
  
    Examples:
    ```c
    my_fun(my_param_expression + 1, 10);
    my_fun2();
    ```
  
  * A separate assignment statement to a variable. The function call
    expression may be negated but is not allowed to be nested in other
    types of expressions.
    
    Examples of **correct** ways of calling yieldable functions:
    ```c
    int var_name_1 = my_fun();
    int var_name_2 = !my_fun();
    var_name_3 = my_fun();
    var_name_4 = !my_fun();
    ```
    
    Examples of **incorrect** ways of calling yieldable functions:
    ```c
    int var_name_1 = (my_fun());
    var_name_2 = 1 + my_fun();
    t->name = my_fun();
    *ptr = my_fun();
    ```

  * As the expression of `while`-statements, `do-while`-statements or
    'if`-statements:

    Examples of **correct** ways of calling yieldable functions:
    ```c
    if(my_fun()) printf("hej\n");
    if(0) else if(my_fun()) printf("hej\n");
    while(!my_fun()) printf("hej\n");
    do { printf("hej\n"); } while(my_fun());
    var_name_3 = my_fun();
    var_name_4 = !my_fun();
    ```
    
    Examples of **incorrect** ways of calling yieldable functions:
    ```
    if(3+my_fun()) printf("hej\n");
    if(hej=my_fun()) printf("hej\n");
    ```
    
    YCF prints a message to standard error and exits with an error
    code if it finds a call to a yieldable function that it cannot
    transform.

Hooks
-----

It is possible to insert special hooks in yieldable functions. Hooks
execute when certain events are happening. Hooks may read and write to
variables (changes to variables are visible after the code block has
executed). Hooks can be placed anywhere one can place a normal
statement within the function body. There are two ways to write hooks:

**Hook Style 1:**

```c
  YCF_SPECIAL_CODE_START(ON_EVENT_NAME);
  printf("This will be printed when EVENT_NAME is happening\n");
  YCF_SPECIAL_CODE_END();
```

**Hook Style 2:**

```c
  /*special_code_start:ON_EVENT_NAME*/
  if(0){
    printf("This will be printed when EVENT_NAME is happening\n");
  }
  /*special_code_end*/
```

The following hook events are currently available:

* `ON_SAVE_YIELD_STATE`

  Triggered when the function yields.

* `ON_RESTORE_YIELD_STATE`

  Triggered before a function resumes after a yield.

* `ON_DESTROY_STATE`

  Triggered if and when the corresponding `_ycf_gen_destroy` function
  is executing.

* `ON_DESTROY_STATE_OR_RETURN`

  Triggered if and when the corresponding `_ycf_gen_destroy` function
  for the function is executing or when the function is returning.

* `ON_RETURN`

  Triggered when the function is returning.

License
-------

Yielding C Fun is released under the [Apache License
2.0](http://www.apache.org/licenses/LICENSE-2.0).


> Copyright Ericsson AB and Kjell Winblad 2019. All Rights Reserved.
>
> Licensed under the Apache License, Version 2.0 (the "License");
> you may not use this file except in compliance with the License.
> You may obtain a copy of the License at
>
>     http://www.apache.org/licenses/LICENSE-2.0
>
> Unless required by applicable law or agreed to in writing, software
> distributed under the License is distributed on an "AS IS" BASIS,
> WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> See the License for the specific language governing permissions and
> limitations under the License.



[1]: http://swtch.com/libtask/ "libtask"
[2]: http://xmailserver.org/libpcl.html
[3]: https://web.archive.org/web/20060110123338/http://www.goron.de/~froese/coro/
[4]: https://github.com/halayli/lthread
[5]: http://dekorte.com/projects/opensource/libcoroutine/
[6]: http://code.google.com/p/libconcurrency/libconcurrency
[7]: http://software.schmorp.de/pkg/libcoro.html
[8]: https://github.com/Adaptv/ribs2
[9]: http://libdill.org/
[10]: https://github.com/hnes/libaco
[11]: https://byuu.org/library/libco/
[12]: http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html
[13]: https://github.com/jsseldenthuis/coroutine