gusimplewhiteboard
gusimplewhiteboard.c
Go to the documentation of this file.
1/*
2 * gusimplewhiteboard.c
3 *
4 * Created by René Hexel on 20/12/11.
5 * Copyright (c) 2011, 2012, 2013, 2014, 2015, 2020 Rene Hexel.
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 *
15 * 2. Redistributions in binary form must reproduce the above
16 * copyright notice, this list of conditions and the following
17 * disclaimer in the documentation and/or other materials
18 * provided with the distribution.
19 *
20 * 3. All advertising materials mentioning features or use of this
21 * software must display the following acknowledgment:
22 *
23 * This product includes software developed by Rene Hexel.
24 *
25 * 4. Neither the name of the author nor the names of contributors
26 * may be used to endorse or promote products derived from this
27 * software without specific prior written permission.
28 *
29 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
30 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
31 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
32 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
33 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
34 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
35 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
36 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
37 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
38 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
39 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40 *
41 * -----------------------------------------------------------------------
42 * This program is free software; you can redistribute it and/or
43 * modify it under the above terms or under the terms of the GNU
44 * General Public License as published by the Free Software Foundation;
45 * either version 2 of the License, or (at your option) any later version.
46 *
47 * This program is distributed in the hope that it will be useful,
48 * but WITHOUT ANY WARRANTY; without even the implied warranty of
49 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
50 * GNU General Public License for more details.
51 *
52 * You should have received a copy of the GNU General Public License
53 * along with this program; if not, see http://www.gnu.org/licenses/
54 * or write to the Free Software Foundation, Inc., 51 Franklin Street,
55 * Fifth Floor, Boston, MA 02110-1301, USA.
56 *
57 */
58#pragma clang diagnostic push
59#pragma clang diagnostic ignored "-Wunused-macros"
60#pragma clang diagnostic ignored "-Wreserved-id-macro"
61#pragma clang diagnostic ignored "-Wclass-varargs"
62#pragma clang diagnostic ignored "-Wbuiltin-requires-header"
63#pragma clang diagnostic ignored "-W#warnings"
64
65#ifdef __linux
66#ifndef _BSD_SOURCE
67#define _BSD_SOURCE
68#endif
69#ifndef _POSIX_SOURCE
70#define _POSIX_SOURCE 200112L
71#endif
72#endif
73#ifndef _XOPEN_SOURCE
74#define _XOPEN_SOURCE
75#endif
76#ifdef __APPLE__
77#ifndef _DARWIN_C_SOURCE
78#define _DARWIN_C_SOURCE 200112L
79#define __DARWIN_C_LEVEL 200112L
80#endif
81#endif
82#include <fcntl.h>
83#include <errno.h>
84#include <stdio.h>
85#include <stdlib.h>
86#include <stdbool.h>
87#include <string.h>
88#include <signal.h>
89
90#undef __block
91#define __block _xblock
92#include <unistd.h>
93#undef __block
94#define __block __attribute__((__blocks__(byref)))
95#pragma clang diagnostic pop
96
97#include <limits.h>
98#include <assert.h>
99#include <sys/types.h>
100#include <sys/param.h>
101#include <sys/file.h>
102#include <sys/mman.h>
103#include <sys/ipc.h>
104#include <sys/sem.h>
105#include <gu_util.h>
106#include "gusimplewhiteboard.h"
107
108#ifdef BUILD_WB_LIBRARY_GUWHITEBOARD
110#else
112#endif
113
114#define WHITEBOARD_MAGIC 0xfeeda11deadbeef5ULL
115#define SEMAPHORE_MAGIC_KEY 4242
116#define SEM_ERROR -1
117
118
119#ifndef SEM_A
120#define SEM_A 0200 /* alter permission */
121#define SEM_R 0400 /* read permission */
122#endif
123
124#ifdef _SEM_SEMUN_UNDEFINED
125union semun
126{
127 int val; // value for SETVAL
128 struct semid_ds *buf; // buffer for IPC_STAT & IPC_SET
129 unsigned short int *array; // array for GETALL & SETALL
130 struct seminfo *__buf; // buffer for IPC_INFO
131};
132#endif
133
135
137{
138 union semun init;
139 init.val = 1;
140 for (enum gsw_semaphores i = 0; i < GSW_NUM_SEM; i++)
141 {
142#ifdef GSW_IOS_DEVICE
143 if (s[i]) dispatch_release(s[i]);
144 s[i] = dispatch_semaphore_create(init.val);
145#else
146#pragma clang diagnostic push
147#pragma clang diagnostic ignored "-Wclass-varargs"
148 if (semctl(s, i, SETVAL, init) == -1)
149 fprintf(stderr, "Warning; failed to initialise whiteboard semaphore %d: %s\n", i, strerror(errno));
150#ifdef DEBUG
151 if (semctl(s, i, GETVAL, NULL) != init.val)
152 fprintf(stderr, "Warning; failed to initialise whiteboard semaphore %d: %s\n", i, strerror(errno));
153#endif
154#pragma clang diagnostic pop
155#endif
156 }
157}
158
159
161{
162#ifdef GSW_IOS_DEVICE
163 gsw_sema_t s = calloc(sizeof(dispatch_semaphore_t), GSW_NUM_SEM);
164 if (!s) return (gsw_sema_t) SEM_ERROR;
166#else
167 int semflg = SEM_R|SEM_A|(SEM_R>>3)|(SEM_A>>3)|(SEM_R>>6)|(SEM_A>>6);
168 gsw_sema_t s = semget(key, GSW_NUM_SEM, semflg);
169
170 if (s == SEM_ERROR)
171 {
172 s = semget(key, GSW_NUM_SEM, semflg | IPC_CREAT);
173 if (s == SEM_ERROR) return s;
174
176 }
177#endif
178 return s;
179}
180
182{
183
184 int nmsgs = GSW_NUM_TYPES_DEFINED;
185 if (nmsgs > GSW_NUM_RESERVED)
186 {
187 fprintf(stderr, "Warning: whiteboard '%s' tries to define %d messages, but only %d reserved\n", name, nmsgs, GSW_NUM_TYPES_DEFINED);
188 nmsgs = GSW_NUM_RESERVED;
189 }
190
192
193 if (wbd == NULL) return NULL;
194
195 char type_str[40];
196 while (nmsgs < GSW_NUM_RESERVED)
197 {
198 snprintf(type_str, sizeof(type_str), "not a type: %d", nmsgs++);
199 gsw_register_message_type(wbd, type_str);
200 }
201
202 return wbd;
203}
204
205
206gu_simple_whiteboard_descriptor *gsw_new_custom_whiteboard(const char *name, const char *message_names[], int num_messages, int semaphore_magic_key)
207{
209 if (!wbd)
210 {
211 fprintf(stderr, "Not enough memory to create whiteboard '%s': %s\n", name, strerror(errno));
212 return NULL;
213 }
214
215 wbd->sem = gsw_setup_semaphores(semaphore_magic_key);
216 if (wbd->sem == (gsw_sema_t) SEM_ERROR)
217 fprintf(stderr, "Warning; cannot get semaphore %d for whiteboard '%s': %s (proceeding without)\n", semaphore_magic_key, name, strerror(errno));
218
219 bool needs_init = false;
220 wbd->wb = gsw_create(name, &wbd->fd, &needs_init);
221 if (!wbd->wb)
222 {
224 return NULL;
225 }
226 if (needs_init)
227 {
229
230 for (int i = 0; i < num_messages; i++)
231 gsw_register_message_type(wbd, message_names[i]);
232 }
233#ifdef WITHOUT_LIBDISPATCH
234 wbd->callback_queue = NULL;
235#else
236 wbd->callback_queue = dispatch_queue_create(NULL, NULL);
237#endif
238 return wbd;
239}
240
242{
243 char name [19]; //allows 999 remote wbs + terminator
244 snprintf(name, 19, "%s%d", GSWR_BASE_NAME, i);
245 return gsw_new_numbered_whiteboard(&name[0], 0);
246}
247
249{
250 return gsw_new_numbered_whiteboard(name, 0);
251}
252
253
255{
256 if (wbd && wbd != local_whiteboard_descriptor)
257 {
259 if (wbd->wb) gsw_free(wbd->wb, wbd->fd);
260#ifndef WITHOUT_LIBDISPATCH
261 if (wbd->callback_queue) dispatch_release(wbd->callback_queue);
262#endif
263 free(wbd);
264 }
265}
266
267static void create_singleton_whiteboard(void *context)
268{
269 const char *name = GSW_DEFAULT_NAME;
270
271 (void)context;
272
273#ifndef GSW_IOS_DEVICE
274 const char *env = getenv(GSW_DEFAULT_ENV);
275 if (env && *env) name = env;
276#endif
277
279}
280
282{
283#ifdef WITHOUT_LIBDISPATCH // not thread-safe without libdispatch!
285 {
287 create_singleton_whiteboard(NULL);
288 }
289#else
290 static dispatch_once_t onceToken;
291 dispatch_once_f(&onceToken, NULL, create_singleton_whiteboard);
292#endif
294}
295
296gu_simple_whiteboard *gsw_create(const char *name, int *fdp, bool *initial)
297{
299 char path[PATH_MAX]
300#ifdef GSW_IOS_DEVICE
301 = "";
302#else
303 = "/tmp/";
304#endif
305 if (!name || strlen(name) > PATH_MAX-strlen(path)-1) name = GSW_DEFAULT_NAME;
306 gu_strlcat(path, name, sizeof(path));
307
308 int fd = open(path, O_CREAT|O_RDWR, 0666);
309 if (fd == -1)
310 {
311 fprintf(stderr, "Cannot open/create '%s': %s\n", path, strerror(errno));
312 return NULL;
313 }
314
315 if (ftruncate(fd, sizeof(gu_simple_whiteboard)) == -1)
316 fprintf(stderr, "Warning; cannot reserve %lu bytes for '%s': %s\n", (unsigned long) sizeof(gu_simple_whiteboard), path, strerror(errno));
317
318 gu_simple_whiteboard *wb = mmap(NULL, sizeof(*wb), PROT_READ|PROT_WRITE, MAP_FILE|MAP_SHARED, fd, 0);
319 if (!wb)
320 {
321 fprintf(stderr, "Cannot mmap '%s': %s\n", path, strerror(errno));
322 close(fd);
323 return NULL;
324 }
325 else if (fdp) *fdp = fd;
326
327 if (wb->magic != WHITEBOARD_MAGIC) // new whiteboard? -> initialise
328 {
329 memset(wb, 0, sizeof(*wb));
332
333 if (initial) *initial = true;
334
335 DBG(printf("New Whiteboard version %d created and initialised at '%s'\n", wb->version, path));
336 }
337 else if (initial) *initial = false;
338
339 bool bailout = wb->version != GU_SIMPLE_WHITEBOARD_VERSION;
340 if (bailout) fprintf(stderr, "*** Unexpected Whiteboard version %d (expected %d for '%s')\n", wb->version, GU_SIMPLE_WHITEBOARD_VERSION, path);
341
342 if (bailout)
343 {
344 gsw_free(wb, fd);
345 return NULL;
346 }
347 return wb;
348}
349
350
352{
353 if (munmap(wb, sizeof(*wb)) == -1)
354 fprintf(stderr, "Cannot unmap whiteboard at %p with fd %d: %s\n", (void *)wb, fd, strerror(errno));
355 if (fd >= 0) if (close(fd) == -1)
356 fprintf(stderr, "Cannot close whiteboard at %p with fd %d: %s\n", (void *)wb, fd, strerror(errno));
357}
358
359
361{
362#ifdef GSW_IOS_DEVICE
363 while (dispatch_semaphore_wait(sem[s], DISPATCH_TIME_FOREVER))
364 {
365 if (errno != EAGAIN)
366 return -1;
367 }
368 return 0;
369#else
370 struct sembuf op = { (unsigned short) s, -1, 0 };
371 int rv;
372 while ((rv = semop(sem, &op, 1)) == -1)
373 if (errno != EAGAIN)
374 break;
375 return rv;
376#endif
377}
378
379
381{
382#ifdef GSW_IOS_DEVICE
383 dispatch_semaphore_signal(sem[s]);
384 return 0;
385#else
386 struct sembuf op = { (unsigned short) s, 1, 0 };
387 int rv;
388 while ((rv = semop(sem, &op, 1)) == -1)
389 if (errno != EAGAIN)
390 break;
391 return rv;
392#endif
393}
394
395
396static u_int32_t hash_of(const char *s)
397{
398 u_int32_t hash = *s;
399 while (*s++)
400 {
401 u_int32_t stir = hash & 0xf8000000U;
402 hash &= 0x07ffffffU;
403 hash <<= 5;
404 hash ^= stir >> 27;
405 hash ^= *s;
406 }
407 return hash;
408}
409
410
411static u_int32_t alt_hash(const char *s)
412{
413 u_int32_t hash = *s;
414 while (*s++)
415 {
416 hash &= 0x0fffffffU;
417 hash <<= 4;
418 hash += *s;
419 u_int32_t stir = hash & 0xf0000000U;
420 if (stir != 0)
421 {
422 hash ^= stir >> 24;
423 hash ^= stir;
424 }
425 }
426 hash |= 1; // needs to be an odd number
427
428 return hash;
429}
430
431
433{
435
436 //bool exists = false;
437 gu_simple_whiteboard *wb = wbd->wb;
438 unsigned offs = hash_of(name) % GSW_TOTAL_MESSAGE_TYPES;
439 gu_simple_message *type = &wb->hashes[offs];
440
442 {
443 type = &wb->hashes[offs];
444 if (!*type->hash.string)
445 {
446 gu_strlcpy(type->hash.string, name, sizeof(type->hash.string));
447 DBG(printf(" - registering wb message type #%d for '%s' at %d\n", wb->num_types, type->hash.string, offs));
448 type->hash.value = wb->num_types++;
449 wb->typenames[type->hash.value] = *type;
450 break;
451 }
452 if (strcmp(type->hash.string, name) == 0)
453 {
454 //exists = true;
455 break;
456 }
457 /* collision, add to the offset */
458 DBG(printf("Hash collision at offset %u: %u == %u %% %d for:\n'%s' <> '%s'",
459 offs, hash_of(name), hash_of(type->hash.string), GSW_TOTAL_MESSAGE_TYPES,
460 name, type->hash.string));
461 offs += alt_hash(name);
463 }
464
466
468 return type->hash.value;
469
470 fprintf(stderr, "Cannot register whiteboard message type '%s': hash table capacity %d reached!\n", name, wb->num_types);
471
472 return -1;
473}
474
475
477{
478 gu_simple_whiteboard *wb = wbd->wb;
479 unsigned offs = hash_of(name) % GSW_TOTAL_MESSAGE_TYPES;
480 gu_simple_message *type; // = &wb->hashes[offs];
481 for (int i = 0; i < GSW_TOTAL_MESSAGE_TYPES; i++)
482 {
483 type = &wb->hashes[offs];
484 if (!*type->hash.string) // new message type?
485 return gsw_register_message_type(wbd, name);
486 if (strcmp(type->hash.string, name) == 0)
487 return type->hash.value;
488 /* hash collision, add to the offset */
489 offs += alt_hash(name);
491 }
492
493 fprintf(stderr, "Cannot get offset for message type '%s': hash table full (%d entries)\n", name, wb->num_types);
494
495 return -1;
496}
497
499{
500 u_int8_t j = wb->indexes[i];
501 if (j >= GU_SIMPLE_WHITEBOARD_GENERATIONS) j = 0;
502 return &wb->messages[i][j];
503}
504
505
507{
508 u_int8_t j = wb->indexes[i];
509 if (++j >= GU_SIMPLE_WHITEBOARD_GENERATIONS) j = 0;
510 return &wb->messages[i][j];
511}
512
513
515{
516 u_int8_t j = wb->indexes[i];
517 if (++j >= GU_SIMPLE_WHITEBOARD_GENERATIONS) j = 0;
518 wb->indexes[i] = j;
519}
520
522{
523 wb->event_counters[i]++; //unsigned, will wrap if exceeds UINT16_MAX
524}
525
526#pragma mark - subscription and callbacks
527
529{
531 gu_simple_whiteboard *wb = wbd->wb;
532 u_int16_t i;
533 for (i = 0; i < wb->subscribed; i++)
534 if (!wb->processes[i] || proc == wb->processes[i])
535 break;
536 if (i < GSW_TOTAL_PROCESSES)
537 {
538 wb->processes[i++] = proc;
539 if (i > wb->subscribed)
540 wb->subscribed = i;
541 }
542 else fprintf(stderr, "Warning: process table full (%d): cannot subscribe %d\n", i, proc);
544}
545
546
548{
550 gu_simple_whiteboard *wb = wbd->wb;
551 u_int16_t i;
552 for (i = 0; i < wb->subscribed; i++)
553 if (proc == wb->processes[i])
554 break;
555 if (i < GSW_TOTAL_PROCESSES)
556 {
557 wb->processes[i] = 0;
558 if (i == wb->subscribed-1)
559 wb->subscribed = i;
560 }
561 else fprintf(stderr, "Warning: cannot remove %d -- process not found!\n", proc);
563}
564
566{
567 wb->eventcount++;
568#ifdef USE_WHITEBOARD_SIGNAL
569 for (int i = 0; i < wb->subscribed; i++)
570 {
571 pid_t proc = wb->processes[i];
572 if (proc) kill(proc, WHITEBOARD_SIGNAL);
573 }
574#endif
575}
576
577#ifndef WITHOUT_LIBDISPATCH
578static void subscription_callback(void *param)
579{
581 if (wbd->callback) wbd->callback(wbd);
582}
583
584static void monitor_subscriptions(void *param)
585{
587 gu_simple_whiteboard *wb = wbd->wb;
588 u_int16_t counter = wb->eventcount;
589 while (!wbd->exit_monitor)
590 {
591 if (counter != wb->eventcount)
592 {
593 counter = wb->eventcount;
594 if (wbd && wbd->callback_queue)
595 dispatch_async_f(wbd->callback_queue, wbd, subscription_callback);
596 }
597 else usleep(WHITEBOARD_POLL_PERIOD);
598 }
599 wbd->got_monitor = false;
600}
601#endif
602
603typedef void (*gsw_sig_t)(int sig);
604static gsw_sig_t old_handler = SIG_DFL;
605
606/* signal handler */
607static void sig_handler(int signum)
608{
609 /* do nothing */
610 (void) signum;
611}
612
614{
616 if (old_handler == SIG_DFL)
617 old_handler = signal(WHITEBOARD_SIGNAL, sig_handler);
618
619#ifndef WITHOUT_LIBDISPATCH // requires libdispatch!
620 if (!wbd->got_monitor)
621 {
622 wbd->got_monitor = true;
623 dispatch_async_f(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), wbd, monitor_subscriptions);
624 }
625#endif
627}
628
629
631{
632 while (wbd->got_monitor)
633 {
634 wbd->exit_monitor = true;
635 usleep(2*WHITEBOARD_POLL_PERIOD);
636 }
637}
gu_simple_whiteboard_descriptor * local_whiteboard_descriptor
gu_simple_message * gsw_next_message(gu_simple_whiteboard *wb, int i)
get the next shared memory location for the given whiteboard message type i
void gsw_add_wbd_signal_handler(gu_simple_whiteboard_descriptor *wbd)
add subscription signal handler
void gsw_free_whiteboard(gu_simple_whiteboard_descriptor *wbd)
free the given whiteboard descriptor
void gsw_free(gu_simple_whiteboard *wb, int fd)
free the whiteboard
gsw_sema_t gsw_setup_semaphores(int key)
set up a semaphore array for the whiteboard
#define SEM_ERROR
void gsw_increment(gu_simple_whiteboard *wb, int i)
get the next shared memory location for the given whiteboard message type i
void gsw_increment_event_counter(gu_simple_whiteboard *wb, int i)
add to a messages event counter on the wb
gu_simple_whiteboard_descriptor * gsw_new_custom_whiteboard(const char *name, const char *message_names[], int num_messages, int semaphore_magic_key)
Access a named, custom whiteboard.
#define SEM_A
void(* gsw_sig_t)(int sig)
gu_simple_whiteboard_descriptor * get_local_singleton_whiteboard(void)
create a simple whiteboard for the local singleton wb pointer
gu_simple_whiteboard_descriptor * gsw_new_whiteboard(const char *name)
access a named whiteboard: this is the designated constructore for C programs
gu_simple_message * gsw_current_message(gu_simple_whiteboard *wb, int i)
get the current shared memory location for the given whiteboard message type i
#define SEM_R
int gsw_procure(gsw_sema_t sem, enum gsw_semaphores s)
grab a whiteboard semaphore
int gsw_offset_for_message_type(gu_simple_whiteboard_descriptor *wbd, const char *name)
get the numerical index of a whiteboard message type
void gsw_init_semaphores(gsw_sema_t s)
initialise the whiteboard semaphores
int gsw_register_message_type(gu_simple_whiteboard_descriptor *wbd, const char *name)
register a new whiteboard message type
#define WHITEBOARD_MAGIC
gu_simple_whiteboard_descriptor * gsw_new_numbered_whiteboard(const char *name, int n)
access a named whiteboard: this is the designated standard wb constructor for C programs that want to...
void gsw_remove_wbd_signal_handler(gu_simple_whiteboard_descriptor *wbd)
remove subscription signal handler
gu_simple_whiteboard * gsw_create(const char *name, int *fdp, bool *initial)
create a simple whiteboard
void gsw_remove_process(gu_simple_whiteboard_descriptor *wbd, const pid_t proc)
remove process for subscription signalling
int gsw_vacate(gsw_sema_t sem, enum gsw_semaphores s)
release a whiteboard semaphore
void gsw_add_process(gu_simple_whiteboard_descriptor *wbd, const pid_t proc)
add process for subscription signalling
#define SEMAPHORE_MAGIC_KEY
gu_simple_whiteboard_descriptor * gswr_new_whiteboard(int i)
access a remote named whiteboard: this is the designated constructore for C programs
void gsw_signal_subscribers(gu_simple_whiteboard *wb)
signal all subscribing processes
#define GSW_DEFAULT_NAME
fallback default wb
#define GU_SIMPLE_WHITEBOARD_GENERATIONS
lifespan (max)
#define GU_SIMPLE_WHITEBOARD_VERSION
version
#define GSW_TOTAL_PROCESSES
maximum subscriber procs
const char ** WBTypes_stringValues
allow whiteboard to use old functions for whiteboard initialisation and choose which messages and con...
#define u_int16_t
int gsw_sema_t
#define GSW_NUM_TYPES_DEFINED
allow whiteboard to use old functions for whiteboard initialisation and choose which messages and con...
#define GSW_DEFAULT_ENV
environment variable containing the default whiteboard file name
#define WHITEBOARD_SIGNAL
#define u_int32_t
#define GSW_TOTAL_MESSAGE_TYPES
message types (max)
#define GSW_NUM_RESERVED
#define u_int8_t
#define WHITEBOARD_POLL_PERIOD
gsw_semaphores
@ GSW_NUM_SEM
number of semaphores
@ GSW_SEM_MSGTYPE
semaphore for message type registration
@ GSW_SEM_PROC
semaphore for process registration
#define GSWR_BASE_NAME
UDP whiteboard name.
#define GU_SIMPLE_WHITEBOARD_BUFSIZE
message len (max)
the actual whiteboard in shared mem
uint16_t version
whiteboard version
uint16_t eventcount
current event count
gu_simple_message typenames[512]
message types for numbers
pid_t processes[256]
list of subscribed processes
uint8_t indexes[512]
ring buffer indexes
gu_simple_message hashes[512]
hashes for registered message types
uint16_t num_types
total number of current, registered types
uint64_t magic
end of whiteboard marker
uint16_t event_counters[512]
event counter loops
gu_simple_message messages[512][4]
the actual messages stored in the whiteboard
uint16_t subscribed
subscribed processes
the underlying whiteboard object
bool got_monitor
have a running monitor
bool exit_monitor
exit the monitor
gsw_subscription_f callback
subscription callback function
dispatch_queue_t callback_queue
subscription callback queue
int fd
the associated memory-mapped file
gsw_sema_t sem
semaphore to use
gu_simple_whiteboard * wb
the actual whiteboard in shared mem
union type that is used to store data in shared memory
char string[128]
string type
struct gsw_simple_message::@3 hash
whiteboard hash type