diff --git a/README.md b/README.md index e2810b02..6a3d61b8 100644 --- a/README.md +++ b/README.md @@ -2021,7 +2021,9 @@ This header is for created snapshots. #### SNAPSHOT\_DEF(name, type[, oplist]) Define the snapshot 'name##\_t' and its associated methods as "static inline" functions. -A snapshot is a mechanism to allows a consumer thread and a produce thread, **working at different speeds**, to exchange messages in a reliable and thread safe way (the message is passed atomatically). +A snapshot is a mechanism to allows a single consumer thread and a single produce thread, + **working at different speeds**, to exchange messages in a reliable and thread safe way +(the message is passed atomatically). In practice, it is implemented using a triple buffer. This container is designed to be used for easy synchronization inter-threads (and the variable shall be a global one). @@ -2096,16 +2098,23 @@ Look at the last atomic snap of the active contain of the snapshot 'snap'. It returns the pointer to the data. This functions is thread-safe and performs atomic operation on the snapshot. -##### type *name\_get\_produced(snapshot\_t snap) +##### bool name\_updated\_p(snapshot\_t snap) + +Return true if the buffer has updated data compared to the last time +it was read. +This functions is thread-safe and performs atomic operation on the snapshot. + +##### type *name\_get\_write\_buffer(snapshot\_t snap) Return a pointer to the active 'in-progress' contain of the snapshot 'snap'. It is the same as the last return from name\_take. This functions is thread-safe and performs atomic operation on the snapshot. -##### type *name\_get\_consummed(snapshot\_t snap) +##### type *name\_get\_read\_buffer(snapshot\_t snap) Return a pointer to the active 'freezed' contain of the snapshot 'snap'. It is the same as the last return from name\_look. +It doesn't perform any switch of the data which has to be read. This functions is thread-safe and performs atomic operation on the snapshot. diff --git a/m-snapshot.h b/m-snapshot.h index 80c2987e..d19039e0 100644 --- a/m-snapshot.h +++ b/m-snapshot.h @@ -93,18 +93,24 @@ //defered #define SNAPSHOTI_DEF(arg) SNAPSHOTI_DEF2 arg +// This is basically an atomic triple buffer between a produced thread +// and a consummer thread. +// TODO: Analyse generalisation to N produced threads and M consummer threads +#define SNAPSHOTI_MAX_BUFFER 3 + #define SNAPSHOTI_DEF2(name, type, oplist) \ \ typedef struct M_C(name, _s) { \ - type data[3]; \ + type data[SNAPSHOTI_MAX_BUFFER]; \ atomic_uchar flags; \ } M_C(name, _t)[1]; \ + \ typedef type M_C(name, _type_t); \ \ static inline void M_C(name, _init)(M_C(name, _t) snap) \ { \ assert(snap != NULL); \ - for(int i = 0; i < 3; i++) { \ + for(int i = 0; i < SNAPSHOTI_MAX_BUFFER; i++) { \ M_GET_INIT oplist(snap->data[i]); \ } \ atomic_init (&snap->flags, SNAPSHOTI_FLAG(0, 1, 2, 0)); \ @@ -114,7 +120,7 @@ static inline void M_C(name, _clear)(M_C(name, _t) snap) \ { \ SNAPSHOTI_CONTRACT(snap); \ - for(int i = 0; i < 3; i++) { \ + for(int i = 0; i < SNAPSHOTI_MAX_BUFFER; i++) { \ M_GET_CLEAR oplist(snap->data[i]); \ } \ } \ @@ -124,7 +130,7 @@ { \ SNAPSHOTI_CONTRACT(org); \ assert(snap != NULL && snap != org); \ - for(int i = 0; i < 3; i++) { \ + for(int i = 0; i < SNAPSHOTI_MAX_BUFFER; i++) { \ M_GET_INIT_SET oplist(snap->data[i], org->data[i]); \ } \ atomic_init (&snap->flags, atomic_load(&org->flags)); \ @@ -136,7 +142,7 @@ { \ SNAPSHOTI_CONTRACT(snap); \ SNAPSHOTI_CONTRACT(org); \ - for(int i = 0; i < 3; i++) { \ + for(int i = 0; i < SNAPSHOTI_MAX_BUFFER; i++) { \ M_GET_SET oplist(snap->data[i], org->data[i]); \ } \ atomic_init (&snap->flags, atomic_load(&org->flags)); \ @@ -149,7 +155,7 @@ { \ SNAPSHOTI_CONTRACT(org); \ assert(snap != NULL && snap != org); \ - for(int i = 0; i < 3; i++) { \ + for(int i = 0; i < SNAPSHOTI_MAX_BUFFER; i++) { \ M_GET_INIT_MOVE oplist(snap->data[i], org->data[i]); \ } \ snap->flags = org->flags; \ @@ -165,7 +171,7 @@ SNAPSHOTI_CONTRACT(snap); \ SNAPSHOTI_CONTRACT(org); \ assert(snap != org); \ - for(int i = 0; i < 3; i++) { \ + for(int i = 0; i < SNAPSHOTI_MAX_BUFFER; i++) { \ M_GET_MOVE oplist(snap->data[i], org->data[i]); \ } \ snap->flags = org->flags; \ @@ -174,17 +180,21 @@ } \ ,) /* IF_METHOD (MOVE) */ \ \ + \ static inline type *M_C(name, _take)(M_C(name, _t) snap) \ { \ SNAPSHOTI_CONTRACT(snap); \ unsigned char nextFlags, origFlags = atomic_load (&snap->flags); \ + /* Atomic CAS operation */ \ do { \ + /* Swap F and W buffer, setting exchange flag */ \ nextFlags = SNAPSHOTI_FLAG(SNAPSHOTI_R(origFlags), \ SNAPSHOTI_F(origFlags), \ SNAPSHOTI_W(origFlags), 1); \ - /* TODO: exponential backoff */ \ + /* FIXME: Is exponential backoff really needed ? */ \ } while (!atomic_compare_exchange_weak (&snap->flags, &origFlags, \ nextFlags)); \ + /* Return new write buffer for new updating */ \ return &snap->data[SNAPSHOTI_W(nextFlags)]; \ } \ \ @@ -192,27 +202,39 @@ { \ SNAPSHOTI_CONTRACT(snap); \ unsigned char nextFlags, origFlags = atomic_load (&snap->flags); \ + /* Atomic CAS operation */ \ do { \ - if (!SNAPSHOTI_B(origFlags)) \ + /* If no exchange registered, do nothing and keep the same */ \ + if (!SNAPSHOTI_B(origFlags)) { \ + nextFlags = origFlags; \ break; \ + } \ + /* Swap R and F buffer, clearing exchange flag */ \ nextFlags = SNAPSHOTI_FLAG(SNAPSHOTI_F(origFlags), \ SNAPSHOTI_W(origFlags), \ SNAPSHOTI_R(origFlags), 0); \ - /* TODO: exponential backoff */ \ + /* FIXME: Is exponential backoff really needed ? */ \ } while (!atomic_compare_exchange_weak (&snap->flags, &origFlags, \ nextFlags)); \ - nextFlags = atomic_load(&snap->flags); \ + /* Return current read buffer */ \ return M_CONST_CAST(type, &snap->data[SNAPSHOTI_R(nextFlags)]); \ } \ \ - static inline type *M_C(name, _get_produced)(M_C(name, _t) snap) \ + static inline bool M_C(name, _updated_p)(M_C(name, _t) snap) \ + { \ + SNAPSHOTI_CONTRACT(snap); \ + unsigned char flags = atomic_load (&snap->flags); \ + return SNAPSHOTI_B(flags); \ + } \ + \ + static inline type *M_C(name, _get_write_buffer)(M_C(name, _t) snap) \ { \ SNAPSHOTI_CONTRACT(snap); \ unsigned char flags = atomic_load(&snap->flags); \ return &snap->data[SNAPSHOTI_W(flags)]; \ } \ \ - static inline const type *M_C(name, _get_consummed)(M_C(name, _t) snap) \ + static inline const type *M_C(name, _get_read_buffer)(M_C(name, _t) snap) \ { \ SNAPSHOTI_CONTRACT(snap); \ unsigned char flags = atomic_load(&snap->flags); \ diff --git a/tests/test-msnapshot.c b/tests/test-msnapshot.c index 7a896e28..b2a36e84 100644 --- a/tests/test-msnapshot.c +++ b/tests/test-msnapshot.c @@ -39,15 +39,17 @@ static void test_uint(void) snapshot_uint_init(t); p_r = snapshot_uint_look(t); - assert (p_r == snapshot_uint_get_consummed(t)); + assert (p_r == snapshot_uint_get_read_buffer(t)); assert(*p_r == 0); assert(snapshot_uint_look(t) == p_r); p_w = snapshot_uint_take(t); + assert (snapshot_uint_updated_p (t) == true); assert (p_w != p_r); *p_w = 1; assert (*p_r == 0); assert (snapshot_uint_look(t) != p_r); + assert (snapshot_uint_updated_p (t) == false); assert (*snapshot_uint_look(t) == 0); p_r = snapshot_uint_look(t); assert (*p_r == 0); @@ -57,7 +59,7 @@ static void test_uint(void) assert (*snapshot_uint_look(t) == 1); p_w = snapshot_uint_take(t); - assert (p_w == snapshot_uint_get_produced(t)); + assert (p_w == snapshot_uint_get_write_buffer(t)); *p_w = 3; p_w = snapshot_uint_take(t); *p_w = 4;