ssm  0.0.2
Runtime Library for the Sparse Synchronous Model
ssm.h
Go to the documentation of this file.
1 /** @file ssm.h
2  * @brief Interface to the SSM runtime.
3  *
4  * This file contains the public-facing interface of the SSM runtime.
5  *
6  * @author Stephen Edwards (sedwards-lab)
7  * @author John Hui (j-hui)
8  */
9 #ifndef _SSM_H
10 #define _SSM_H
11 
12 #include <stdbool.h> /* For bool, true, false */
13 #include <stddef.h> /* For offsetof and size_t */
14 #include <stdint.h> /* For uint16_t, UINT64_MAX etc. */
15 
16 #ifdef CONFIG_MEM_TRACE
17 #include <stdio.h>
18 #endif
19 
20 /**
21  * @addtogroup error
22  * @{
23  */
24 
25 /** @brief Error codes indicating reason for failure.
26  *
27  * Platforms may extend the list of errors using #SSM_PLATFORM_ERROR like this:
28  *
29  * ~~~{.c}
30  * enum {
31  * SSM_CUSTOM_ERROR_CODE1 = SSM_PLATFORM_ERROR,
32  * SSM_CUSTOM_ERROR_CODE2,
33  * // etc.
34  * };
35  * ~~~
36  */
37 typedef enum ssm_error {
39  /**< Reserved for unforeseen, non-user-facing errors. */
41  /**< Tried to insert into full activation record queue. */
43  /**< Tried to insert into full event queue. */
45  /**< Could not allocate more memory. */
47  /**< Tried to exceed available recursion depth. */
49  /**< Not yet ready to perform the requested action. */
51  /**< Specified invalid time, e.g., scheduled assignment at an earlier time. */
53  /**< Invalid memory layout, e.g., using a pointer where int was expected. */
55  /**< Start of platform-specific error code range. */
57 
58 /** @brief Terminate due to a non-recoverable error, with a specified reason.
59  *
60  * Invoked when a process must terminate, e.g., when memory or queue space is
61  * exhausted. Not expected to terminate.
62  *
63  * Wraps the underlying ssm_throw() exception handler, passing along the
64  * file name, line number, and function name in the source file where the error
65  * was thrown.
66  *
67  * @param reason an #ssm_error_t specifying the reason for the error.
68  */
69 #define SSM_THROW(reason) ssm_throw(reason, __FILE__, __LINE__, __func__)
70 
71 /** @brief Underlying exception handler; must be overridden by each platform.
72  *
73  * This is left undefined by the SSM runtime for portability. On platforms
74  * where exit() is available, that may be used. Where possible, an exception
75  * handler may also log additional information using the given parameters for
76  * better debuggability.
77  *
78  * @param reason an #ssm_error_t specifying the reason for the error.
79  * @param file the file name of the source file where the error was thrown.
80  * @param line the line number of the source file where the error was thrown.
81  * @param func the function name where the error was thrown.
82  */
83 void ssm_throw(ssm_error_t reason, const char *file, int line,
84  const char *func);
85 
86 /** @} */
87 
88 struct ssm_mm;
89 struct ssm_sv;
90 struct ssm_trigger;
91 struct ssm_act;
92 
93 /**
94  * @addtogroup act
95  * @{
96  */
97 
98 /** @brief Thread priority.
99  *
100  * Lower numbers execute first in an instant.
101  */
102 typedef uint32_t ssm_priority_t;
103 
104 /** @brief The priority for the entry point of an SSM program. */
105 #define SSM_ROOT_PRIORITY 0
106 
107 /** @brief Index of least significant bit in a group of priorities
108  *
109  * This only needs to represent the number of bits in the #ssm_priority_t type.
110  */
111 typedef uint8_t ssm_depth_t;
112 
113 /** @brief The depth at the entry point of an SSM program. */
114 #define SSM_ROOT_DEPTH (sizeof(ssm_priority_t) * 8)
115 
116 /** @brief The function that does an instant's work for a routine. */
117 typedef void ssm_stepf_t(struct ssm_act *);
118 
119 /** @brief Activation record for an SSM routine.
120  *
121  * Routine activation record "base class." A struct for a particular
122  * routine must start with this type but then may be followed by
123  * routine-specific fields.
124  */
125 typedef struct ssm_act {
126  ssm_stepf_t *step; /**< C function for running this continuation. */
127  struct ssm_act *caller; /**< Activation record of caller. */
128  uint16_t pc; /**< Stored "program counter" for the function. */
129  uint16_t children; /**< Number of running child threads. */
130  ssm_priority_t priority; /**< Execution priority; lower goes first. */
131  ssm_depth_t depth; /**< Index of the LSB in our priority. */
132  bool scheduled; /**< True when in the schedule queue. */
134 
135 /** @brief Indicates a routine should run when a scheduled variable is written.
136  *
137  * Node in linked list of activation records, maintained by each scheduled
138  * variable to determine which continuations should be scheduled when the
139  * variable is updated.
140  */
141 typedef struct ssm_trigger {
142  struct ssm_trigger *next; /**< Next sensitive trigger, if any. */
143  struct ssm_trigger **prev_ptr; /**< Pointer to self in previous element. */
144  struct ssm_act *act; /**< Routine triggered by this variable. */
146 
147 /** @brief Whether an activation record has any children. */
148 #define ssm_has_children(act) ((act)->children != 0)
149 
150 /** @brief Allocate and initialize a routine activation record.
151  *
152  * Uses the underlying memory allocator to allocate an activation record of
153  * a given @a size, and initializes it with the rest of the fields. Also
154  * increments the number of children that @a parent has.
155  *
156  * This function assumes that the embedded #ssm_act_t is at the beginning of
157  * the allocated activation record. That is, it has the following layout:
158  *
159  * ~~~{.c}
160  * struct {
161  * ssm_act_t act;
162  * // Other fields
163  * };
164  * ~~~
165  *
166  * @param size the size of the routine activation record to allocate.
167  * @param step the step function of the routine.
168  * @param parent the parent (caller) of the activation record.
169  * @param priority the priority of the activation record.
170  * @param depth the depth of the activation record.
171  * @returns the newly initialized activation record.
172  *
173  * @throws SSM_EXHAUSTED_MEMORY out of memory.
174  */
175 ssm_act_t *ssm_enter_int(size_t size, ssm_stepf_t step, ssm_act_t *parent,
177 
178 #ifdef CONFIG_MEM_TRACE
179 #define ssm_enter(si, st, pa, pr, de) \
180  (fprintf(stderr, "%s:%d:ssm_enter(%lu,_,_,_,_,_)\n", __FILE__, __LINE__, \
181  (si)), \
182  ssm_enter_int((si), (st), (pa), (pr), (de)))
183 #else
184 #define ssm_enter(si, st, pa, pr, de) \
185  ssm_enter_int((si), (st), (pa), (pr), (de))
186 #endif
187 
188 /** @brief Destroy the activation record of a routine before leaving.
189  *
190  * Calls the parent if @a act is the last child.
191  *
192  * @param act the activation record of the routine to leave.
193  * @param size the size of activation record.
194  */
195 void ssm_leave(ssm_act_t *act, size_t size);
196 
197 /** @brief Schedule a routine to run in the current instant.
198  *
199  * This function is idempotent: it may be called multiple times on the same
200  * activation record within an instant; only the first call has any effect.
201  *
202  * @param act activation record of the routine to schedule.
203  *
204  * @throws SSM_EXHAUSTED_ACT_QUEUE the activation record is full.
205  */
206 void ssm_activate(ssm_act_t *act);
207 
208 /** @brief An activation record for the parent of the top-most routine.
209  *
210  * This activation record should be used as the parent of the entry point.
211  * For example, if the entry point is named `main`, with activation record type
212  * `main_act_t` and step function `step_main`:
213  *
214  * ~~~{.c}
215  * ssm_enter(sizeof(main_act_t), step_main, &ssm_top_parent,
216  * SSM_ROOT_PRIORITY, SSM_ROOT_DEPTH)
217  * ~~~
218  */
220 
221 /** @} */
222 
223 /**
224  * @addtogroup mem
225  * @{
226  */
227 
228 /** @brief Values are 32-bits, the largest supported machine word size. */
229 typedef uint32_t ssm_word_t;
230 
231 /** @brief SSM values are either "packed" values or heap-allocated. */
232 typedef union {
233  struct ssm_mm *heap_ptr; /**< Pointer to a heap-allocated object. */
234  ssm_word_t packed_val; /**< Packed value. */
235 } ssm_value_t;
236 
237 /** @brief The memory management metadata "header" for heap-allocated objects.
238  *
239  * This header should always be embedded in heap-allocated objects as the first
240  * field (at memory offset 0); values of type #ssm_value_t will point to this
241  * header and use its @a kind and other fields to figure out the size and
242  * memory layout of the rest of the object.
243  *
244  * The interpretation and usage of the latter 16 bits of this header, i.e., the
245  * @a info field, depends on the value of @a kind. For objects encoding
246  * variants (e.g., #ssm_adt1), the @a count field counts the number of fields
247  * in the object payload, while the @a tag records which variant is inhabited
248  * by the object. Meanwhile, vector-style objects (e.g., #ssm_closure1) also
249  * use the @a count field to record the number of values present, but use the
250  * @a cap field to determine the full capacity of the payload. Finally, objects
251  * like arrays and blobs do not need to record more than their size, and
252  * benefit from making use of all 16 bits to support up to 65536 sizes.
253  */
254 struct ssm_mm {
255  uint8_t ref_count; /**< The number of references to this object. */
256  uint8_t kind; /**< The #ssm_kind of object this is. */
257  union {
258  struct {
259  uint8_t count; /**< Number of #ssm_value_t values in payload. */
260  uint8_t tag; /**< Which variant is inhabited by this object. */
262  struct {
263  uint8_t count; /**< Number of #ssm_value_t values in payload. */
264  uint8_t cap; /**< Which variant is inhabited by this object. */
266  uint16_t size; /**< 16-bit size */
267  } info; /**< Three "flavors" of information embedded in the header. */
268 };
269 
270 /** @brief The different kinds of heap objects, enumerated.
271  *
272  * Types enumerated here that are not ADTs are chosen because they cannot be
273  * easily or efficiently expressed as a product of words. For instance, 64-bit
274  * timestamps cannot be directly stored in the payload of a regular heap
275  * object, where even-numbered timestamps may be misinterpreted as pointers.
276  */
277 enum ssm_kind {
278  SSM_TIME_K = 0, /**< 64-bit timestamps, #ssm_time_t */
279  SSM_ADT_K, /**< ADT object, e.g., #ssm_adt1 */
280  SSM_SV_K, /**< Scheduled variables, #ssm_sv_t */
281  SSM_CLOSURE_K, /**< Closure object, e.g., #ssm_closure1 */
282  SSM_ARRAY_K, /**< Array of values, e.g., #ssm_array1 */
283  SSM_BLOB_K, /**< Blob of arbitrary data, e.g., #ssm_blob1 */
284 };
285 
286 /** @brief Construct an #ssm_value_t from a 31-bit integral value.
287  *
288  * @param v the 31-bit integral value.
289  * @return a packed #ssm_value_t.
290  */
291 #define ssm_marshal(v) \
292  (ssm_value_t) { .packed_val = ((v) << 1 | 1) }
293 
294 /** @brief Extract an integral value from a packed #ssm_value_t.
295  *
296  * @param v the packed #ssm_value_t.
297  * @return the raw 31-bit integral value.
298  */
299 #define ssm_unmarshal(v) ((v).packed_val >> 1)
300 
301 /** @brief Whether a value is on the heap (i.e., is an object).
302  *
303  * @param v pointer to the #ssm_value_t.
304  * @returns non-zero if on heap, zero otherwise.
305  */
306 #define ssm_on_heap(v) (((v).packed_val & 0x1) == 0)
307 
308 /** @brief Whether a value is shared, i.e., unsafe to modify.
309  *
310  * The opposite of "shared" is "unique," as in C++'s "unique_ptr."
311  *
312  * @param v pointer to the #ssm_value_t.
313  * @returns non-zero if shared, zero otherwise.
314  */
315 #define ssm_is_shared(v) !(ssm_on_heap(v) && ((v).heap_ptr->ref_count == 1))
316 
317 /** @brief Duplicate a possible heap reference, incrementing its ref count.
318  *
319  * If the caller knows @a v is definitely on the heap, call ssm_drop_unsafe()
320  * to eliminate the heap check (and omit call if it is definitely not on the
321  * heap).
322  *
323  * @param v pointer to the #ssm_mm header of the heap item.
324  */
325 #define ssm_dup(v) (ssm_on_heap(v) ? ssm_dup_unsafe(v) : (v))
326 
327 /** @brief Drop a reference to a possible heap item, and free it if necessary.
328  *
329  * If @a v is freed, all references held by the heap item itself will also be
330  * be dropped.
331  *
332  * If the caller knows that @a v is definitely a heap item, call
333  * ssm_drop_unsafe() to eliminate the heap check (and omit call if it is
334  * definitely not on the heap).
335  *
336  * @param v #ssm_value_t to be dropped.
337  */
338 #define ssm_drop(v) \
339  do \
340  if (ssm_on_heap(v)) \
341  ssm_drop_unsafe(v); \
342  while (0)
343 
344 /** @brief Duplicate a heap reference, incrementing its ref count.
345  *
346  * Called by ssm_dup().
347  *
348  * @note assumes that @a v is a heap pointer, i.e., `ssm_on_heap(v)`.
349  *
350  * @param v pointer to the #ssm_mm header of the heap item.
351  */
352 #define ssm_dup_unsafe(v) ((++(v).heap_ptr->ref_count, (v)))
353 
354 /** @brief Drop a reference to a heap item, and free it if necessary.
355  *
356  * If @a v is freed, ssm_drop_final() will be called to drop all heap objects
357  * @a v refers to.
358  *
359  * Called by ssm_drop().
360  *
361  * @note assumes that @a v is a heap pointer, i.e., `ssm_on_heap(v)`.
362  *
363  * @param v #ssm_value_t to be dropped.
364  * @returns 0 on success.
365  */
366 #define ssm_drop_unsafe(v) \
367  do \
368  if (--(v).heap_ptr->ref_count == 0) \
369  ssm_drop_final(v); \
370  while (0)
371 
372 /** @brief Finalize and free a heap object.
373  *
374  * Drops all heap objects @a v refers to, and perform any additional
375  * finalization as required by the #ssm_kind of @a v.
376  *
377  * Called by ssm_drop_unsafe().
378  *
379  * @note assumes that @a v is a heap pointer, i.e., `ssm_on_heap(v)`.
380  *
381  * @param v #ssm_value_t to be finalized and freed.
382  */
384 
385 /** @brief Call ssm_dup() on an array of values. */
386 void ssm_dups(size_t cnt, ssm_value_t *arr);
387 
388 /** @brief Call ssm_drop() on an array of values. */
389 void ssm_drops(size_t cnt, ssm_value_t *arr);
390 
391 /** @} */
392 
393 /**
394  * @addtogroup time
395  * @{
396  */
397 
398 /** @brief Absolute time; never to overflow. */
399 typedef uint64_t ssm_time_t;
400 
401 /** @brief Heap-allocated time values.
402  *
403  * These should never be declared on their own, and should only be allocated on
404  * the heap using ssm_new_time().
405  *
406  * @invariant for all `struct ssm_time t`, `t.mm.kind == SSM_TIME_K`.
407  */
408 struct ssm_time {
409  struct ssm_mm mm; /**< Embedded memory management header. */
410  ssm_time_t time; /**< Time value payload. */
411 };
412 
413 /** @brief Time indicating something will never happen.
414  *
415  * The value of this must be derived from the underlying type of #ssm_time_t.
416  */
417 #define SSM_NEVER UINT64_MAX
418 
419 /** Ticks per nanosecond */
420 #define SSM_NANOSECOND 1L
421 /** Ticks per microsecond */
422 #define SSM_MICROSECOND (SSM_NANOSECOND * 1000L)
423 /** Ticks per millisecond */
424 #define SSM_MILLISECOND (SSM_MICROSECOND * 1000L)
425 /** Ticks per second */
426 #define SSM_SECOND (SSM_MILLISECOND * 1000L)
427 /** Ticks per minute */
428 #define SSM_MINUTE (SSM_SECOND * 60L)
429 /** Ticks per hour */
430 #define SSM_HOUR (SSM_MINUTE * 60L)
431 
432 /** @brief The current model time.
433  *
434  * @returns the current model time.
435  */
436 ssm_time_t ssm_now(void);
437 
438 /** @brief Allocate a #ssm_time on the heap.
439  *
440  * @param time what the heap-allocated @a time field is initialized to.
441  * @returns #ssm_value_t pointing to the heap-allocated #ssm_time.
442  *
443  * ssm_value_t ssm_new_time(ssm_time_t time)
444  */
446 #ifdef CONFIG_MEM_TRACE
447 #define ssm_new_time(t) \
448  (fprintf(stderr, "%s:%d:ssm_new_time()\n", __FILE__, __LINE__), \
449  ssm_new_time_int(t))
450 #else
451 #define ssm_new_time(t) ssm_new_time_int(t)
452 #endif
453 
454 /** @brief Read the heap-allocated time pointed to by an #ssm_value_t.
455  *
456  * @note The behavior of using this macro on an #ssm_value_t that does not
457  * point to an #ssm_time is undefined.
458  * @note The behavior of using the result of this macro as an l-value is
459  * undefined.
460  *
461  * @param v the #ssm_value_t
462  * @returns the #ssm_time_t in the heap.
463  */
464 #define ssm_time_read(v) (container_of((v).heap_ptr, struct ssm_time, mm)->time)
465 
466 /** @} */
467 
468 /**
469  * @addtogroup adt
470  * @{
471  */
472 
473 /** @brief The struct template of a heap-allocated ADT object.
474  *
475  * This ADT struct is meant to be used as a template for performing other
476  * ADT-related pointer arithmetic; no instance of this should ever be declared.
477  *
478  * Though this struct's @a fields is only declared with 1 #ssm_value_t, actual
479  * heap-allocated ADT objects may have more fields. For instance, an object
480  * with 3 fields might look like:
481  *
482  * ~~~{.c}
483  * struct ssm_adt3 {
484  * struct ssm_mm mm;
485  * ssm_value_t fields[3];
486  * };
487  * ~~~
488  *
489  * Note that the memory layout of all ADTs is the same save for the length of
490  * the @a fields, so we use this struct definition as the "base case" of ADT
491  * object lengths.
492  */
493 struct ssm_adt1 {
494  struct ssm_mm mm; /**< Variant-flavored memory management header. */
495  ssm_value_t fields[1]; /**< Array of ADT object fields. */
496 };
497 
498 /** @brief Allocate a new ADT object on the heap.
499  *
500  * @note This function fully initializes the #ssm_mm header of the ADT object,
501  * but leaves its fields uninitialized. It is the responsibility of the
502  * caller to properly <em>all</em> @a val_count fields.
503  *
504  * @param field_count the number of fields in the ADT object.
505  * @param tag the tag of the ADT object, stored in the #ssm_mm header.
506  * @returns #ssm_value_t poining to the ADT object on the heap.
507  */
508 extern ssm_value_t ssm_new_adt_int(uint8_t field_count, uint8_t tag);
509 
510 #ifdef CONFIG_MEM_TRACE
511 #define ssm_new_adt(fc, tag) \
512  (fprintf(stderr, "%s:%d:ssm_new_adt(%d, %d)\n", __FILE__, __LINE__, (fc), \
513  (tag)), \
514  ssm_new_adt_int((fc), (tag)))
515 #else
516 #define ssm_new_adt(fc, tag) ssm_new_adt_int((fc), (tag))
517 #endif
518 
519 /** @brief Access the field of an ADT object.
520  *
521  * The result of this macro can also be used as an l-value, i.e., it may be
522  * assigned to, e.g.,:
523  *
524  * ~~~{.c}
525  * ssm_adt_field(v, 0) = ssm_marshal(1);
526  * ~~~
527  *
528  * @note The behavior of using this macro on an #ssm_value_t that does not
529  * point to an ADT object of sufficient size (@a val_count greater than
530  * @a i) is undefined.
531  *
532  * @param v the #ssm_value_t pointing to a heap-allocated ADT object.
533  * @param i the 0-base index of the field in @a v to be accessed.
534  * @returns the @a i'th field of @a v.
535  */
536 #define ssm_adt_field(v, i) \
537  (&*container_of((v).heap_ptr, struct ssm_adt1, mm)->fields)[i]
538 
539 /** @brief Retrieve the tag of an ADT object.
540  *
541  * @note The behavior of using this macro on an #ssm_value_t that points to
542  * anything other than an ADT object is undefined.
543  *
544  * @param v the #ssm_value_t whose tag is being retrieved.
545  * @returns the tag of @a v.
546  */
547 #define ssm_tag(v) \
548  (ssm_on_heap(v) ? (v).heap_ptr->info.variant.tag : ssm_unmarshal(v))
549 
550 /** @brief Obtain number of fields in the ADT pointed by @a v. */
551 #define ssm_adt_field_count(v) ((v).heap_ptr->info.variant.count)
552 
553 /** @brief Compute the size of a heap-allocated ADT.
554  *
555  * @param val_count the @a val_count field of the ADT object's #ssm_mm header.
556  * @returns the size of the ADT object in the heap.
557  */
558 #define ssm_adt_size(val_count) \
559  (sizeof(struct ssm_adt1) + sizeof(ssm_value_t) * ((val_count)-1))
560 
561 /** @brief Compute the size of an ADT already allocated on the heap. */
562 #define ssm_adt_heap_size(v) ssm_adt_size(ssm_adt_field_count(v))
563 
564 /** @} */
565 
566 /**
567  * @addtogroup sv
568  * @{
569  */
570 
571 /** @brief A scheduled variable that supports scheduled updates with triggers.
572  *
573  * Scheduled variables are heap-allocated built-in types that represent
574  * variables with reference-like semantics in SSM. These should be always
575  * allocated on the heap using ssm_new_sv().
576  *
577  * Routines may directly assign to them in the current instant (ssm_assign()),
578  * or schedule a delayed assignment to them (ssm_later()). The @a last_updated
579  * time is recorded in each case, sensitive routines, i.e., @a triggers, are
580  * woken up.
581  *
582  * At most one delayed assignment may be scheduled at a time, but a single
583  * update may wake any number of sensitive routines.
584  *
585  * @invariant @a later_time != #SSM_NEVER iff this variable in the event queue.
586  * @invariant for all `ssm_sv_t v`, `v.mm.kind == SSM_SV_K`.
587  */
588 typedef struct ssm_sv {
589  struct ssm_mm mm;
590  ssm_time_t later_time; /**< When the variable should be next updated. */
591  ssm_time_t last_updated; /**< When the variable was last updated. */
592  ssm_trigger_t *triggers; /**< List of sensitive continuations. */
593  ssm_value_t value; /**< Current value. */
594  ssm_value_t later_value; /**< Buffered value for delayed assignment. */
596 
597 /** @brief Allocate an #ssm_sv on the heap.
598  *
599  * @note Initialization does not count as an update event; @a last_updated is
600  * initialized to #SSM_NEVER.
601  * @note The @a later_value field is left uninitialized.
602  *
603  * @param val the initial value of the scheduled variable.
604  * @returns #ssm_value_t pointing to the #ssm_sv on the heap.
605  */
607 #ifdef CONFIG_MEM_TRACE
608 #define ssm_new_sv(v) \
609  (fprintf(stderr, "%s:%d:ssm_new_sv()\n", __FILE__, __LINE__), \
610  ssm_new_sv_int(v))
611 #else
612 #define ssm_new_sv(v) ssm_new_sv_int(v)
613 #endif
614 
615 /** @brief Retrieve #ssm_sv pointer pointed to by an #ssm_value_t.
616  *
617  * @note The behavior of using this macro on an #ssm_value_t that does not
618  * point to a scheduled variable is undefined.
619  *
620  * @param val the #ssm_value_t
621  * @returns pointer to the #ssm_sv_t
622  */
623 #define ssm_to_sv(val) container_of((val).heap_ptr, ssm_sv_t, mm)
624 
625 /** @brief Read the value of a scheduled variable.
626  *
627  * @note The behavior of using this macro on an #ssm_value_t that does not
628  * point to a scheduled variable is undefined.
629  * @note The behavior of using the result of this macro as an l-value is
630  * undefined. To assign to a scheduled variable, use ssm_assign() or
631  * ssm_later().
632  *
633  * @param val #ssm_value_t that points to a scheduled variable.
634  * @returns the value that @a val points to.
635  */
636 #define ssm_deref(val) (ssm_to_sv(val)->value)
637 
638 /** @brief Instantaneous assignment to a scheduled variable.
639  *
640  * Updates the value of @a var in the current instant, and wakes up all
641  * sensitive processes at a lower priority than the calling routine.
642  *
643  * ssm_assign() will ssm_drop() the previous value of @a var and ssm_dup() the
644  * new @a val. If the caller already knows whether @a var and @a val reside on
645  * the heap, they may call ssm_assign_unsafe() to forego the ssm_drop() and
646  * ssm_dup() calls, but will be responsible for reference counting themselves
647  * (i.e., calling ssm_dup_unsafe() and ssm_drop_unsafe() for heap objects).
648  *
649  * @note Does not overwrite a scheduled assignment.
650  * @note The behavior of this macro when @a var does not point to a scheduled
651  * variable is undefined.
652  *
653  * @param var pointer to the scheduled variable.
654  * @param prio priority of the calling routine.
655  * @param val the value to be assigned to @a var.
656  */
657 #define ssm_assign(var, prio, val) \
658  do { \
659  ssm_dup(val); \
660  ssm_drop(ssm_deref(var)); \
661  ssm_sv_assign_unsafe(ssm_to_sv(var), prio, val); \
662  } while (0)
663 
664 /** @brief Delayed assignment to a scheduled variable.
665  *
666  * Schedules a delayed assignment to @a var at a later time.
667  *
668  * ssm_later() will ssm_dup() the scheduled @a val. If the caller already
669  * knows whether @a val resides on the heap, they may call ssm_later_unsafe()
670  * to forego the ssm_dup(), but will be responsible for reference counting
671  * themselves (i.e., calling ssm_dup_unsafe() for heap objects).
672  *
673  * Overwrites any previously scheduled update, if any.
674  *
675  * @note The behavior of this macro when @a var does not point to a scheduled
676  * variable is undefined.
677  *
678  * @param var pointer to the scheduled variable.
679  * @param when the time when the update should take place.
680  * @param val the value to be assigned to @a var.
681  *
682  * @throws SSM_INVALID_TIME @a later is greater than ssm_now().
683  * @throws SSM_EXHAUSTED_EVENT_QUEUE event queue ran out of space.
684  */
685 #define ssm_later(var, when, val) \
686  do { \
687  ssm_dup(val); \
688  ssm_sv_later_unsafe(ssm_to_sv(var), when, val); \
689  } while (0)
690 
691 /** @brief ssm_assign() without automatic reference counting.
692  *
693  * @note Does not overwrite a scheduled assignment.
694  *
695  * @param var pointer to the scheduled variable.
696  * @param prio priority of the calling routine.
697  * @param val the value to be assigned to @a var.
698  *
699  * @sa ssm_assign().
700  */
702 
703 /** @brief ssm_later() without automatic reference counting.
704  *
705  * Overwrites any previously scheduled update, if ssm_laterr
706  *
707  * @note ssm_drop() <em>will</em> be called on the previous @a later_value,
708  * so the caller is not responsible for checking that.
709  *
710  * @param var pointer to the scheduled variable.
711  * @param when the time when the update should take place.
712  * @param val the value to be assigned to @a var.
713  *
714  * @throws SSM_INVALID_TIME @a later is greater than ssm_now().
715  * @throws SSM_EXHAUSTED_EVENT_QUEUE event queue ran out of space.
716  *
717  * @sa ssm_later().
718  */
719 void ssm_sv_later_unsafe(ssm_sv_t *var, ssm_time_t when, ssm_value_t val);
720 
721 /** @brief Sensitize a trigger to a scheduled variable.
722  *
723  * Macro provided for convenience.
724  *
725  * @sa ssm_sv_sensitize().
726  *
727  * @param var #ssm_value_t pointing to a scheduled variable.
728  * @param trig trigger to be registered on @a var.
729  */
730 #define ssm_sensitize(var, trig) ssm_sv_sensitize(ssm_to_sv(var), trig)
731 
732 /** @brief Desensitize a trigger.
733  *
734  * Macro provided for convenience.
735  *
736  * @sa ssm_sv_desensitize().
737  *
738  * @param trig the trigger.
739  */
740 #define ssm_desensitize(trig) ssm_sv_desensitize(trig)
741 
742 /** @brief Sensitize a variable to a trigger.
743  *
744  * Adds a trigger to a scheduled variable, so that @a trig's activation
745  * record is awoken when the variable is updated.
746  *
747  * This function should be called by a routine before sleeping (yielding).
748  *
749  * @param var pointer to the scheduled variable.
750  * @param trig trigger to be registered on @a var.
751  */
752 void ssm_sv_sensitize(ssm_sv_t *var, ssm_trigger_t *trig);
753 
754 /** @brief Desensitize a variable from a trigger.
755  *
756  * Remove a trigger from its variable.
757  *
758  * This function should be called by a routine after returning from sleeping.
759  *
760  * @param trig the trigger.
761  */
763 
764 /** @} */
765 
766 /**
767  * @addtogroup closure
768  * @{
769  */
770 
771 /** @brief The type signature for all SSM enter functions.
772  *
773  * This type standardizes the "calling convention" of SSM functions, so that
774  * they can be stored in generic closures and treated uniformly.
775  *
776  * @param parent the activation record of the caller.
777  * @param prio the priority of the callee.
778  * @param depth the depth of the callee.
779  * @param argv an array of #ssm_value_t arguments given to the callee.
780  * @param ret the return value address that the callee should write to.
781  * @returns an allocated activation record for the callee.
782  */
783 typedef ssm_act_t *(*ssm_func_t)(ssm_act_t *parent, ssm_priority_t prio,
784  ssm_depth_t depth, ssm_value_t *argv,
785  ssm_value_t *ret);
786 
787 /** @brief The struct template of a heap-allocated closure object.
788  *
789  * This struct is meant to be used as a template for performing other
790  * closure-related pointer arithmetic; no instance of this should ever be
791  * declared. See elaborated discussion about this pattern for #ssm_adt1.
792  *
793  * A closure should always be allocated with enough space in @a argv to
794  * accommodate <em>all</em> arguments expected by the enter function @a f,
795  * so faciliate efficient in-place usage.
796  *
797  * The current number of arguments owned by the closure is tracked by the
798  * memory management header's @a val_count field; the reference counts of the
799  * @a argv fields within this range need to be decremented when the enclosing
800  * closure is freed. The total number of arguments accommodated by the closure
801  * is tracked by the @a tag field; this also determines the size of the closure
802  * object.
803  */
804 struct ssm_closure1 {
805  struct ssm_mm mm; /**< Memory management header. */
806  ssm_func_t f; /**< Enter function pointer. */
807  ssm_value_t argv[1]; /**< An array of arguments. */
808 };
809 
810 /** @brief Obtain the number of argument values owned by a closure. */
811 #define ssm_closure_arg_count(v) \
812  (container_of((v).heap_ptr, struct ssm_closure1, mm)->mm.info.vector.count)
813 
814 /** @brief Obtain the number of argument values accommodated by a closure. */
815 #define ssm_closure_arg_cap(v) \
816  (container_of((v).heap_ptr, struct ssm_closure1, mm)->mm.info.vector.cap)
817 
818 /** @brief Obtain the enter function pointer of a closure. */
819 #define ssm_closure_func(v) \
820  (container_of((v).heap_ptr, struct ssm_closure1, mm)->f)
821 
822 /** @brief Retrieve the argument array of a closure. */
823 #define ssm_closure_argv(v) \
824  (&*container_of((v).heap_ptr, struct ssm_closure1, mm)->argv)
825 
826 /** @brief Obtain the ith argument of a closure. */
827 #define ssm_closure_arg(v, i) ssm_closure_argv(v)[i]
828 
829 /** @brief Compute the size of a closure object.
830  *
831  * @param val_count the @a val_count field of the closure's #ssm_mm header.
832  * @returns the size of the closure object.
833  */
834 #define ssm_closure_size(val_count) \
835  (sizeof(struct ssm_closure1) + (sizeof(ssm_value_t) * ((val_count)-1)))
836 
837 /** @brief Compute the size of a closure already allocated on the heap. */
838 #define ssm_closure_heap_size(v) ssm_closure_size(ssm_closure_arg_cap(v))
839 
840 /** @brief Add an argument to a closure.
841  *
842  * Note that this helper increments the @a arg_count of @a closure but does not
843  * perform memory management, i.e., does not ssm_dup() @a arg. It also does not
844  * check whether @a closure has capacity for @a arg.
845  */
846 #define ssm_closure_push(closure, arg) \
847  do \
848  ssm_closure_arg(closure, ssm_closure_arg_count(closure)++) = (arg); \
849  while (0)
850 
851 /** @brief Remove an argument from a closure.
852  *
853  * Note that this helper decrements the @a arg_count of @a closure but does not
854  * perform memory management, i.e., does not ssm_drop() @a arg. It also does
855  * not check whether @a closure has a non-zero number of arguments already
856  * applied.
857  */
858 #define ssm_closure_pop(closure) \
859  do \
860  ssm_closure_arg_count(closure)--; \
861  while (0)
862 
863 /** @brief Spawn and schedule a new child process from a fully-applied closure.
864  *
865  * Note that this helper does not perform any memory management, nor does it
866  * ensure that @a closure has all arguments applied.
867  */
868 #define ssm_closure_activate(closure, parent, prio, depth, ret) \
869  ssm_activate(ssm_closure_func(closure)(parent, prio, depth, \
870  ssm_closure_argv(closure), ret))
871 
872 /** @brief Allocate a closure on the heap.
873  *
874  * @param f the enter function pointer of the closure.
875  * @param arg_cap the number of arguments the closure should accommodate.
876  * @returns a value pointing to the heap-allocated closure.
877  */
878 extern ssm_value_t ssm_new_closure_int(ssm_func_t f, uint8_t arg_cap);
879 #ifdef CONFIG_MEM_TRACE
880 #define ssm_new_closure(f, args) \
881  (fprintf(stderr, "%s:%d:ssm_new_closure(_, %d)\n", __FILE__, __LINE__, \
882  (args)), \
883  ssm_new_closure_int((f), (args)))
884 #else
885 #define ssm_new_closure(f, args) ssm_new_closure_int((f), (args))
886 #endif
887 
888 /** @brief Create a copy of a closure.
889  *
890  * @note Calls ssm_dup() on all previously applied arguments.
891  *
892  * @param closure the closure to be copied.
893  * @returns a value pointing to the heap-allocated closure.
894  */
896 
897 /** @brief Apply an argument to a closure.
898  *
899  * The behavior depends on whether @a closure needs to be activated after being
900  * applied to @a arg, i.e., whether all of its arguments have arrived.
901  *
902  * If @a closure is activated, a child process will be scheduled, so the caller
903  * must yield to the scheduler to allow the child process to run. The child
904  * process will write its return value to @a ret.
905  *
906  * If @a closure is not activated, a copy of it (via ssm_closure_clone()) with
907  * @a arg pushed to its @a argv will be written to @a ret.
908  *
909  * In both cases, all values in @a closure's @a argv will have ssm_dup() called
910  * on them, since application will result in those values being shared in a new
911  * context, either from a child process or from the cloned closure. Note that
912  * ssm_dup() is <em>not</em> called on @a arg; to do so automatically, use
913  * ssm_closure_apply_auto().
914  *
915  * @param closure the closure to be applied.
916  * @param arg the argument @a closure is applied to.
917  * @param parent the current process performing the application.
918  * @param prio priority of the current process.
919  * @param depth depth of the current process.
920  * @param ret pointer to the return value.
921  */
922 void ssm_closure_apply(ssm_value_t closure, ssm_value_t arg, ssm_act_t *parent,
923  ssm_priority_t prio, ssm_depth_t depth,
924  ssm_value_t *ret);
925 
926 /** @brief Apply an argument to a closure that is used for the last time.
927  *
928  * An optimized version of ssm_closure_apply(), to be used only provided:
929  *
930  * 1. this is the last time @a closure is used by the caller.
931  * 2. no other processes share @a closure.
932  *
933  * This implementation should be used as the fast path when those conditions
934  * hold; it improves upon the more general ssm_closure_apply() by using @a
935  * closure in-place, and avoiding unnecessary ssm_dup() and ssm_drop() called.
936  * After this is called, @a closure will not need to be dropped.
937  *
938  * The behavior of this function is equivalent to:
939  *
940  * ```{.c}
941  * ssm_closure_apply(f, a, p, prio, depth, ret);
942  * ssm_drop(f);
943  * ```
944  *
945  * Like ssm_closure_apply(), this function does not ssm_drop() @a arg; to do so
946  * automatically, use ssm_closure_apply_final_auto().
947  *
948  * @param closure the closure to be applied.
949  * @param arg the argument @a closure is applied to.
950  * @param parent the current process performing the application.
951  * @param prio priority of the current process.
952  * @param depth depth of the current process.
953  * @param ret pointer to the return value.
954  */
956  ssm_act_t *parent, ssm_priority_t prio,
957  ssm_depth_t depth, ssm_value_t *ret);
958 
959 /** @brief Closure application with automatic memory management.
960  *
961  * ssm_dup() is called on @a a before @a f is applied to it;
962  * see ssm_closure_apply().
963  *
964  * @param closure the closure to be applied.
965  * @param arg the argument @a closure is applied to.
966  * @param parent the current process performing the application.
967  * @param prio priority of the current process.
968  * @param depth depth of the current process.
969  * @param ret pointer to the return value.
970  */
971 #define ssm_closure_apply_auto(closure, arg, parent, prio, depth, ret) \
972  do { \
973  ssm_dup(arg); \
974  ssm_closure_apply(closure, arg, parent, prio, depth, ret); \
975  } while (0)
976 
977 /** @brief In-place closure application with automatic memory management.
978  *
979  * ssm_dup() is called on @a a before @a f is applied to it;
980  * see ssm_closure_apply_final().
981  *
982  * @param closure the closure to be applied.
983  * @param arg the argument @a closure is applied to.
984  * @param parent the current process performing the application.
985  * @param prio priority of the current process.
986  * @param depth depth of the current process.
987  * @param ret pointer to the return value.
988  */
989 #define ssm_closure_apply_final_auto(closure, arg, parent, prio, depth, ret) \
990  do { \
991  ssm_dup(arg); \
992  ssm_closure_apply_final(closure, arg, parent, prio, depth, ret); \
993  } while (0)
994 
995 /** @brief Helper to free a closure (without reference counting). */
996 #define ssm_closure_free(closure) \
997  ssm_mem_free((closure).heap_ptr, \
998  ssm_closure_size(ssm_closure_arg_cap(closure)))
999 
1000 /** @} */
1001 
1002 /**
1003  * @addtogroup array
1004  * @{
1005  */
1006 
1007 /** @brief The struct template of a heap-allocated array of values.
1008  *
1009  */
1010 struct ssm_array1 {
1011  struct ssm_mm mm; /**< Size-flavored memory management header. */
1012  ssm_value_t elements[1]; /**< Elements of the heap-allocated array. */
1013 };
1014 
1015 /** @brief The length of an array pointed by @a v. */
1016 #define ssm_array_len(v) ((v).heap_ptr->info.size)
1017 
1018 /** @brief Obtain pointer to the array elements payload pointed to by @ a v. */
1019 #define ssm_array_elements(v) \
1020  (&*(container_of((v).heap_ptr, struct ssm_array1, mm)->elements))
1021 
1022 /** @brief Obtain pointer to the ith element of the array pointed by @a v. */
1023 #define ssm_array_element(v, i) (ssm_array_elements(v)[i])
1024 
1025 /** @brief Compute the size of an array with its header.
1026  *
1027  * @param count the number of array elements, i.e., the @a size field.
1028  * @returns size that a array of @a count elemtns occupies in the heap.
1029  */
1030 #define ssm_array_size(count) \
1031  (sizeof(struct ssm_array1) + sizeof(ssm_value_t) * ((count)-1))
1032 
1033 /** @brief Compute the size an array in the heap from an #ssm_value_t.
1034  *
1035  * @param v #ssm_value_t pointing to some blob in the heap.
1036  * @returns size of the array that @a v points to.
1037  */
1038 #define ssm_array_heap_size(v) ssm_array_size(ssm_array_len(v))
1039 
1040 /** @brief Allocate an array on the heap.
1041  *
1042  * Note that this function returns an array with all elements uninitialized,
1043  * which must be initialized before ssm_drop() can be called on the array.
1044  *
1045  * @param count number of #ssm_value_t elements to be stored in the array.
1046  * @returns #ssm_value_t pointing to heap-allocated array.
1047  */
1048 
1049 extern ssm_value_t ssm_new_array_int(uint16_t count);
1050 #ifdef CONFIG_MEM_TRACE
1051 #define ssm_new_array(c) \
1052  (fprintf(stderr,"%s:%d:ssm_new_array(%d)\n", \
1053  __FILE__, __LINE__, (c)), \
1054  ssm_new_array_int(c)
1055 #else
1056 #define ssm_new_array(c) ssm_new_array_int(c)
1057 #endif
1058 
1059 /** @} */
1060 
1061 /**
1062  * @addtogroup blob
1063  * @{
1064  */
1065 
1066 /** @brief The @a size resolution for heap-allocated blobs. */
1067 #define SSM_BLOB_SIZE_SCALE 4
1068 
1069 /** @brief The struct template of a heap-allocated blob.
1070  *
1071  * Blobs are just arbitrary chunks of memory in the heap where any kind of
1072  * data can be stored, with any layout. Since the memory manager will not scan
1073  * blobs for pointers, any resources maintained within blobs must be managed
1074  * via other means.
1075  *
1076  * Though this struct's @a payload is only declared with 4 bytes, actual
1077  * heap-allocated blobs may have large payloads. For instance, a 48-byte blob
1078  * might look like:
1079  *
1080  * ~~~{.c}
1081  * struct ssm_blob48 {
1082  * struct ssm_mm mm;
1083  * char payload[48 / SSM_BLOB_SIZE_SCALE];
1084  * };
1085  * ~~~
1086  *
1087  * The memory layout of all blobs is the same save for the size of the
1088  * @a payload, so we use this struct definition as the "base case" of blobs.
1089  *
1090  * To allow even larger blobs, the actual size of the payload is divided by
1091  * #SSM_BLOB_SIZE_SCALE while stored in the @a size field.
1092  */
1093 struct ssm_blob1 {
1094  struct ssm_mm mm; /**< Size-flavored memory management header. */
1095  char payload[SSM_BLOB_SIZE_SCALE]; /**< Payload of heap-allocated blob. */
1096 };
1097 
1098 /** @brief Compute the size of a blob with its header.
1099  *
1100  * The @a size parameter should already be scaled, i.e., already multiplied by
1101  * #SSM_BLOB_SIZE_SCALE, before being passed into this macro.
1102  *
1103  * @param size scaled size of the blob's payload.
1104  * @returns size that a blob of @a size payload occupies in the heap.
1105  */
1106 #define ssm_blob_size(size) \
1107  (sizeof(struct ssm_blob1) - SSM_BLOB_SIZE_SCALE + (size))
1108 
1109 /** @brief Compute the size a blob in the heap.
1110  *
1111  * @param v #ssm_value_t pointing to some blob in the heap.
1112  * @returns size of the blob that @a v points to.
1113  */
1114 #define ssm_blob_heap_size(v) \
1115  ssm_blob_size(((v).heap_ptr->info.size) * SSM_BLOB_SIZE_SCALE)
1116 
1117 /** @brief Obtain pointer to the payload of a blob from an #ssm_value_t. */
1118 #define ssm_blob_payload(v) \
1119  (&*(container_of((v).heap_ptr, struct ssm_blob1, mm)->payload))
1120 
1121 /** @brief Allocate a blob on the heap.
1122  *
1123  * @param size size of the payload to the allocated.
1124  * @returns #ssm_value_t pointing to heap-allocated blob.
1125  */
1126 extern ssm_value_t ssm_new_blob_int(uint16_t size);
1127 #ifdef CONFIG_MEM_TRACE
1128 #define ssm_new_blob(s) \
1129  (fprintf(stderr, "%s:%d:ssm_new_blob(%lu)\n", __FILE__, __LINE__, (size)), \
1130  ssm_new_blob_int(size))
1131 #else
1132 #define ssm_new_blob(s) ssm_new_blob_int(s)
1133 #endif
1134 
1135 /** @} */
1136 
1137 /**
1138  * @addtogroup mem
1139  * @{
1140  */
1141 
1142 /** @brief Allocate a contiguous range of memory.
1143  *
1144  * Memory will be allocated from an appropriately sized memory pool, if one
1145  * is available. Guaranteed to be aligned against the smallest power of 2
1146  * greater than @a size.
1147  *
1148  * @param size the requested memory range size, in bytes.
1149  * @returns pointer to the first byte of the allocate memory block.
1150  */
1151 void *ssm_mem_alloc(size_t size);
1152 
1153 /** @brief Preallocate memory pages to ensure capacity in memory pools.
1154  *
1155  * Does nothing if no memory pool will fit a block of @a size.
1156  *
1157  * @param size size whose memory pool should be preallocaed pages.
1158  * @param num_pages number of pages to allocate.
1159  */
1160 void ssm_mem_prealloc(size_t size, size_t num_pages);
1161 
1162 /** @brief Deallocate memory allocated by ssm_mem_alloc().
1163  *
1164  * The behavior of freeing memory not allocated by ssm_mem_alloc() is
1165  * undefined.
1166  *
1167  * @param m pointer to the memory range allocated by ssm_mem_alloc().
1168  * @param size the size of the memory range allocated by ssm_mem_alloc().
1169  */
1170 void ssm_mem_free(void *m, size_t size);
1171 
1172 /** @} */
1173 
1174 /** @ingroup util
1175  * @def member_type
1176  * @brief Obtain the type of a struct member.
1177  *
1178  * Intended for use in #container_of, for type safety.
1179  *
1180  * When GNU C is not available, falls back to ISO C99 and returns `const void`
1181  * (see https://stackoverflow.com/a/10269925/10497710).
1182  *
1183  * For instance, given the following struct definition:
1184  *
1185  * ~~~{.c}
1186  * struct foo { int bar; float baz; };
1187  * ~~~
1188  *
1189  * `member_type(struct foo, baz)` expands to `float`.
1190  *
1191  * @param type the struct type.
1192  * @param member the name of the member in @a type.
1193  * @returns the type of @a member in @a type.
1194  */
1195 #ifdef __GNUC__
1196 #define member_type(type, member) __typeof__(((type *)0)->member)
1197 #else
1198 #define member_type(type, member) const void
1199 #endif
1200 
1201 /** @ingroup util
1202  * @brief Obtain the pointer to an outer, enclosing struct.
1203  *
1204  * For example, with the following struct definition:
1205  *
1206  * ~~~{.c}
1207  * struct foo { int bar; float baz; };
1208  * ~~~
1209  *
1210  * Given some `float *p`, `container_of(p, struct foo, baz)` will return
1211  * a pointer to the enclosing `struct foo`. The caller is responsible for
1212  * ensuring that such an enclosing pointer actually exists.
1213  *
1214  * Use this macro instead of pointer casting or pointer arithmetic; this macro
1215  * is more principled in what it does and, with GNU C support, will trigger
1216  * compiler warnings when @a ptr and the @a member type do not agree.
1217  *
1218  * Adapted from the Linux kernel source
1219  * (see https://stackoverflow.com/a/10269925/10497710).
1220  *
1221  * @param ptr pointer to member.
1222  * @param type type of enclosing struct.
1223  * @param member name of member in enclosing struct.
1224  * @returns pointer to enclosing struct.
1225  */
1226 #define container_of(ptr, type, member) \
1227  ((type *)((char *)(member_type(type, member) *){ptr} - \
1228  offsetof(type, member)))
1229 
1230 #endif
struct ssm_trigger ssm_trigger_t
Indicates a routine should run when a scheduled variable is written.
uint8_t ssm_depth_t
Index of least significant bit in a group of priorities.
Definition: ssm.h:111
void ssm_leave(ssm_act_t *act, size_t size)
Destroy the activation record of a routine before leaving.
ssm_act_t * ssm_enter_int(size_t size, ssm_stepf_t step, ssm_act_t *parent, ssm_priority_t priority, ssm_depth_t depth)
Allocate and initialize a routine activation record.
struct ssm_act ssm_act_t
Activation record for an SSM routine.
void ssm_stepf_t(struct ssm_act *)
The function that does an instant's work for a routine.
Definition: ssm.h:117
ssm_act_t ssm_top_parent
An activation record for the parent of the top-most routine.
Definition: ssm-top-act.c:9
uint32_t ssm_priority_t
Thread priority.
Definition: ssm.h:102
void ssm_activate(ssm_act_t *act)
Schedule a routine to run in the current instant.
ssm_value_t ssm_new_adt_int(uint8_t field_count, uint8_t tag)
Allocate a new ADT object on the heap.
Definition: ssm-mem.c:274
ssm_value_t ssm_new_array_int(uint16_t count)
Allocate an array on the heap.
Definition: ssm-mem.c:305
ssm_value_t ssm_new_blob_int(uint16_t size)
Allocate a blob on the heap.
Definition: ssm-mem.c:313
#define SSM_BLOB_SIZE_SCALE
The size resolution for heap-allocated blobs.
Definition: ssm.h:1067
void ssm_closure_apply(ssm_value_t closure, ssm_value_t arg, ssm_act_t *parent, ssm_priority_t prio, ssm_depth_t depth, ssm_value_t *ret)
Apply an argument to a closure.
Definition: ssm-closure.c:25
ssm_value_t ssm_new_closure_int(ssm_func_t f, uint8_t arg_cap)
Allocate a closure on the heap.
Definition: ssm-mem.c:294
ssm_value_t ssm_closure_clone(ssm_value_t closure)
Create a copy of a closure.
Definition: ssm-closure.c:12
void ssm_closure_apply_final(ssm_value_t closure, ssm_value_t arg, ssm_act_t *parent, ssm_priority_t prio, ssm_depth_t depth, ssm_value_t *ret)
Apply an argument to a closure that is used for the last time.
Definition: ssm-closure.c:43
ssm_act_t *(* ssm_func_t)(ssm_act_t *parent, ssm_priority_t prio, ssm_depth_t depth, ssm_value_t *argv, ssm_value_t *ret)
The type signature for all SSM enter functions.
Definition: ssm.h:783
void ssm_throw(ssm_error_t reason, const char *file, int line, const char *func)
Underlying exception handler; must be overridden by each platform.
enum ssm_error ssm_error_t
Error codes indicating reason for failure.
ssm_error
Error codes indicating reason for failure.
Definition: ssm.h:37
@ SSM_EXHAUSTED_ACT_QUEUE
Tried to insert into full activation record queue.
Definition: ssm.h:40
@ SSM_INVALID_MEMORY
Invalid memory layout, e.g., using a pointer where int was expected.
Definition: ssm.h:52
@ SSM_EXHAUSTED_PRIORITY
Tried to exceed available recursion depth.
Definition: ssm.h:46
@ SSM_INTERNAL_ERROR
Reserved for unforeseen, non-user-facing errors.
Definition: ssm.h:38
@ SSM_EXHAUSTED_MEMORY
Could not allocate more memory.
Definition: ssm.h:44
@ SSM_EXHAUSTED_EVENT_QUEUE
Tried to insert into full event queue.
Definition: ssm.h:42
@ SSM_NOT_READY
Not yet ready to perform the requested action.
Definition: ssm.h:48
@ SSM_PLATFORM_ERROR
Start of platform-specific error code range.
Definition: ssm.h:54
@ SSM_INVALID_TIME
Specified invalid time, e.g., scheduled assignment at an earlier time.
Definition: ssm.h:50
ssm_kind
The different kinds of heap objects, enumerated.
Definition: ssm.h:277
void ssm_mem_prealloc(size_t size, size_t num_pages)
Preallocate memory pages to ensure capacity in memory pools.
uint32_t ssm_word_t
Values are 32-bits, the largest supported machine word size.
Definition: ssm.h:229
void ssm_mem_free(void *m, size_t size)
Deallocate memory allocated by ssm_mem_alloc().
Definition: ssm-mem.c:233
void ssm_drops(size_t cnt, ssm_value_t *arr)
Call ssm_drop() on an array of values.
Definition: ssm-mem.c:372
void ssm_drop_final(ssm_value_t v)
Finalize and free a heap object.
Definition: ssm-mem.c:325
void * ssm_mem_alloc(size_t size)
Allocate a contiguous range of memory.
Definition: ssm-mem.c:183
void ssm_dups(size_t cnt, ssm_value_t *arr)
Call ssm_dup() on an array of values.
Definition: ssm-mem.c:367
@ SSM_BLOB_K
Blob of arbitrary data, e.g., ssm_blob1.
Definition: ssm.h:283
@ SSM_ARRAY_K
Array of values, e.g., ssm_array1.
Definition: ssm.h:282
@ SSM_CLOSURE_K
Closure object, e.g., ssm_closure1.
Definition: ssm.h:281
@ SSM_TIME_K
64-bit timestamps, ssm_time_t
Definition: ssm.h:278
@ SSM_SV_K
Scheduled variables, ssm_sv_t.
Definition: ssm.h:280
@ SSM_ADT_K
ADT object, e.g., ssm_adt1.
Definition: ssm.h:279
void ssm_sv_desensitize(ssm_trigger_t *trig)
Desensitize a variable from a trigger.
struct ssm_sv ssm_sv_t
A scheduled variable that supports scheduled updates with triggers.
void ssm_sv_later_unsafe(ssm_sv_t *var, ssm_time_t when, ssm_value_t val)
ssm_later() without automatic reference counting.
ssm_value_t ssm_new_sv_int(ssm_value_t val)
Allocate an ssm_sv on the heap.
Definition: ssm-mem.c:283
void ssm_sv_sensitize(ssm_sv_t *var, ssm_trigger_t *trig)
Sensitize a variable to a trigger.
void ssm_sv_assign_unsafe(ssm_sv_t *var, ssm_priority_t prio, ssm_value_t val)
ssm_assign() without automatic reference counting.
ssm_time_t ssm_now(void)
The current model time.
ssm_value_t ssm_new_time_int(ssm_time_t time)
Allocate a ssm_time on the heap.
Definition: ssm-mem.c:266
uint64_t ssm_time_t
Absolute time; never to overflow.
Definition: ssm.h:399
Activation record for an SSM routine.
Definition: ssm.h:125
ssm_priority_t priority
Execution priority; lower goes first.
Definition: ssm.h:130
uint16_t children
Number of running child threads.
Definition: ssm.h:129
ssm_depth_t depth
Index of the LSB in our priority.
Definition: ssm.h:131
struct ssm_act * caller
Activation record of caller.
Definition: ssm.h:127
ssm_stepf_t * step
C function for running this continuation.
Definition: ssm.h:126
uint16_t pc
Stored "program counter" for the function.
Definition: ssm.h:128
bool scheduled
True when in the schedule queue.
Definition: ssm.h:132
The struct template of a heap-allocated ADT object.
Definition: ssm.h:493
ssm_value_t fields[1]
Array of ADT object fields.
Definition: ssm.h:495
struct ssm_mm mm
Variant-flavored memory management header.
Definition: ssm.h:494
The struct template of a heap-allocated array of values.
Definition: ssm.h:1010
ssm_value_t elements[1]
Elements of the heap-allocated array.
Definition: ssm.h:1012
struct ssm_mm mm
Size-flavored memory management header.
Definition: ssm.h:1011
The struct template of a heap-allocated blob.
Definition: ssm.h:1093
char payload[SSM_BLOB_SIZE_SCALE]
Payload of heap-allocated blob.
Definition: ssm.h:1095
struct ssm_mm mm
Size-flavored memory management header.
Definition: ssm.h:1094
The struct template of a heap-allocated closure object.
Definition: ssm.h:804
ssm_func_t f
Enter function pointer.
Definition: ssm.h:806
ssm_value_t argv[1]
An array of arguments.
Definition: ssm.h:807
struct ssm_mm mm
Memory management header.
Definition: ssm.h:805
The memory management metadata "header" for heap-allocated objects.
Definition: ssm.h:254
uint8_t cap
Which variant is inhabited by this object.
Definition: ssm.h:264
uint8_t count
Number of ssm_value_t values in payload.
Definition: ssm.h:259
struct ssm_mm::@0::@1 variant
uint8_t tag
Which variant is inhabited by this object.
Definition: ssm.h:260
union ssm_mm::@0 info
Three "flavors" of information embedded in the header.
uint8_t ref_count
The number of references to this object.
Definition: ssm.h:255
uint8_t kind
The ssm_kind of object this is.
Definition: ssm.h:256
struct ssm_mm::@0::@2 vector
uint16_t size
16-bit size
Definition: ssm.h:266
A scheduled variable that supports scheduled updates with triggers.
Definition: ssm.h:588
ssm_time_t later_time
When the variable should be next updated.
Definition: ssm.h:590
ssm_time_t last_updated
When the variable was last updated.
Definition: ssm.h:591
ssm_value_t value
Current value.
Definition: ssm.h:593
ssm_trigger_t * triggers
List of sensitive continuations.
Definition: ssm.h:592
ssm_value_t later_value
Buffered value for delayed assignment.
Definition: ssm.h:594
struct ssm_mm mm
Definition: ssm.h:589
Heap-allocated time values.
Definition: ssm.h:408
ssm_time_t time
Time value payload.
Definition: ssm.h:410
struct ssm_mm mm
Embedded memory management header.
Definition: ssm.h:409
Indicates a routine should run when a scheduled variable is written.
Definition: ssm.h:141
struct ssm_trigger ** prev_ptr
Pointer to self in previous element.
Definition: ssm.h:143
struct ssm_act * act
Routine triggered by this variable.
Definition: ssm.h:144
struct ssm_trigger * next
Next sensitive trigger, if any.
Definition: ssm.h:142
SSM values are either "packed" values or heap-allocated.
Definition: ssm.h:232
struct ssm_mm * heap_ptr
Pointer to a heap-allocated object.
Definition: ssm.h:233
ssm_word_t packed_val
Packed value.
Definition: ssm.h:234