#include #include struct queue_entry { size_t entry_length; void* entry_contents; struct queue_entry* entry_next; }; // Global state static int loopback_cap = -1; static HsStablePtr loopback_mvar = NULL; static struct queue_entry * queue = NULL; static Mutex loopback_mutex; // Signal to the listening thread that there are chunks to read void tryWakeup(void) { if(loopback_mvar) { hs_try_putmvar(loopback_cap, loopback_mvar); loopback_mvar = NULL; } } void registerWakeup(int cap, HsStablePtr mvar) { loopback_cap = cap; loopback_mvar = mvar; // immediately wake it up if the queue is not empty if (queue) { tryWakeup(); } } void pushChunk(void* chunk, size_t chunk_size) { ACQUIRE_LOCK(loopback_mutex); struct queue_entry* tail = queue; // This is O(N) #1 should fix this while(tail && tail->entry_next) { tail = tail->entry_next; } struct queue_entry* entry = malloc(sizeof(struct queue_entry)); entry->entry_length = chunk_size; entry->entry_contents = chunk; entry->entry_next = NULL; if(tail) { // append to the end of the queue ASSERT(tail->entry_next == NULL); tail->entry_next = entry; } else { // queue is empty queue = entry; } RELEASE_LOCK(loopback_mutex); } void freeChunk(struct queue_entry* entry) { if(entry){ // entry->entry_contents will be freed on the Haskell side free(entry); } } struct queue_entry* popChunk() { ACQUIRE_LOCK(loopback_mutex); struct queue_entry* entry = queue; if(entry) { queue = entry->entry_next; } RELEASE_LOCK(loopback_mutex); return entry; } // Initialise the loopback writer void loopback_init(void) { initMutex(&loopback_mutex); } bool loopback_write(void* eventlog, size_t eventlog_size) { void* chunk = malloc(eventlog_size); memcpy(chunk, eventlog, eventlog_size); pushChunk(chunk, eventlog_size); tryWakeup(); return true; } void loopback_flush(void) { } void loopback_stop(void) { loopback_cap = -1; loopback_mvar = NULL; // TODO: free everything on the queue? queue = NULL; #if defined(THREADED_RTS) closeMutex(&loopback_mutex); #endif } EventLogWriter loopback_writer = { .initEventLogWriter = loopback_init, .writeEventLog = loopback_write, .flushEventLog = loopback_flush, .stopEventLogWriter = loopback_stop }; void startLoopbackWriter(void) { endEventLogging(); startEventLogging(&loopback_writer); } void stopLoopbackWriter(void) { loopback_stop(); endEventLogging(); }