{"id":40317919,"url":"https://github.com/fredrikwidlund/libreactorng","last_synced_at":"2026-01-20T07:04:28.528Z","repository":{"id":175382749,"uuid":"653806085","full_name":"fredrikwidlund/libreactorng","owner":"fredrikwidlund","description":"libreactor is a high performance, robust and secure, generic event-driven application framework for Linux","archived":false,"fork":false,"pushed_at":"2025-04-05T11:01:27.000Z","size":197,"stargazers_count":89,"open_issues_count":0,"forks_count":8,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-04-05T11:32:11.515Z","etag":null,"topics":["application-framework","c","event-driven","high-availability","high-performance","linux","robust","scalability","secure","web-framework"],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fredrikwidlund.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-06-14T19:22:48.000Z","updated_at":"2025-04-05T11:01:31.000Z","dependencies_parsed_at":"2024-03-06T16:11:57.349Z","dependency_job_id":null,"html_url":"https://github.com/fredrikwidlund/libreactorng","commit_stats":null,"previous_names":["fredrikwidlund/libreactorng"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/fredrikwidlund/libreactorng","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fredrikwidlund%2Flibreactorng","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fredrikwidlund%2Flibreactorng/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fredrikwidlund%2Flibreactorng/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fredrikwidlund%2Flibreactorng/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fredrikwidlund","download_url":"https://codeload.github.com/fredrikwidlund/libreactorng/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fredrikwidlund%2Flibreactorng/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28597985,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-20T02:08:49.799Z","status":"ssl_error","status_checked_at":"2026-01-20T02:08:44.148Z","response_time":117,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["application-framework","c","event-driven","high-availability","high-performance","linux","robust","scalability","secure","web-framework"],"created_at":"2026-01-20T07:04:28.445Z","updated_at":"2026-01-20T07:04:28.521Z","avatar_url":"https://github.com/fredrikwidlund.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"![CodeQL](https://github.com/fredrikwidlund/libreactorng/actions/workflows/codeql.yml/badge.svg)\n![Build](https://github.com/fredrikwidlund/libreactorng/actions/workflows/c-cpp.yml/badge.svg)\n\n# ![](https://github.com/fredrikwidlund/libreactorng/assets/2116262/834b278c-23e2-4688-a312-23ba965dba01) libreactor\n\n## About\n\nlibreactor is a [high performance](#performance), [robust and secure](#security), generic event-driven application framework for Linux. The design goal is to minimize software overhead, cpu usage, energy consumption and environmental footprint. libreactor is directly built on top of the [Linux kernel io_uring](https://kernel.dk/io_uring.pdf) system call interface, offering both much simplified access to low level asynchronous kernel calls, as well as high level event-driven abstractions such as HTTP servers. Furthermore libreactor is built completely without third-party dependencies, minimizing supply chain risk.\n\n## Key Features\n\n- Data types such as [data vectors](#data-vectors), [buffers](#buffers), [lists](#lists), [dynamic arrays](#vectors), [hash tables](#maps), [UTF-8 strings](#utf-8-strings), [JSON values (including RFC 8259 compliant serialization)](#json), [memory pools](#memory-pools)\n- Generic [event driven framework](#events)\n- Support for [threads](#threads)\n- Low level [io_uring abstrations](#io-uring)\n- High level event abstrations such as [signals](#signals), [timers](#timers), [file system event notifiers](#notify), [buffered streams](#streams)\n- Network abstractions supporting [servers](#servers), [clients](#clients) and [resolvers](#resolvers)\n- [HTTP framework](#http)\n- Multi-producer multi-consumer [message queues](#queues)\n- Declarative graph based data flow application framework\n\n## Performance\n\nThe current version of libreactor is completely refactored to use the Linux kernel io_uring system call interface, achieving a clear performance jump from the previous epoll()-based version that has been a top contender of the [Techempower benchmark](https://www.techempower.com/benchmarks/#section=data-r21\u0026test=json) for many years (the benchmark is in itself flawed in many ways but should still give an indication of performance potential).\n\nA fast framework can help you achieve performance, but it can not replace the need for optimized software design and architecture. If your software design is flawed from a performance perspective you will not achieve high performance and high availability, regardless of implementation details.\n\n## Security\n\nThe libreactor pipeline is built with unit-tests that require 100% line coverage, and 100% branch coverage (the latter probably in itself indicates an OCD-diagnosis) to succeed. The above tests are also completed using Valgrind to ensure memory management hygiene. Static analysis is done using CodeQL.\n\nAlthough this is a refactored version of libreactor, the previous version has been running in production as web API servers 24/7/365 for almost 10 years serving many millions of unique end-users, at consistently very high RPS rates, actually with perfect availability so far. Since there are few moving parts, no real third-party dependencies, and the kernel API is very stable (perhaps less so with io_uring) change management is typically very simple. \n\n## Installation\n\n``` sh\ngit clone https://github.com/fredrikwidlund/libreactorng.git\ncd libreactorng/\n./autogen.sh\n./configure\nmake \u0026\u0026 sudo make install\n```\n\n## Usage\n\n```sh\ncat \u003e main.c \u003c\u003cEOF\n#include \u003creactor.h\u003e\n\nint main()\n{\n  buffer_t b;\n\n  buffer_construct(\u0026b);\n  buffer_load(\u0026b, \"/etc/motd\");\n  buffer_prepend(\u0026b, data_string(\"--- first line ---\\n\"));\n  buffer_append(\u0026b, data_string(\"--- last line ---\\n\"));\n  buffer_save(\u0026b, \"/tmp/motd\");\n  buffer_destruct(\u0026b);\n}\nEOF\ngcc -Wall -o main main.c `pkg-config --libs --cflags libreactor`\n./main\n```\n\n## Data types\n\n### Data vectors\n\nData vectors are generic data containers reducing the need for the common use case of handling separate variables for pointers and size. Data vectors also remove the need for zero-terminated strings, and help reduce strlen() runtime usage.\n\n#### Example\n\n```C\nvoid out(data_t d)\n{\n  write(1, data_base(d), data_size(d));\n}\n\nint main()\n{\n  data_t d = data_string(\"compiled with \\\"-flto -O2\\\" this application will not call strlen() later down the call stack\\n\");\n  out(d);\n}                                                                                                                                                    ```\n```\n\n### Buffers\n\nBuffers offers generic data containers with dynamic memory allocation. Buffers can inserted into, erased from, resized and compacted, saved to and loaded from files.\n\n#### Example\n\n```C\nint main()\n{\n  buffer_t b;\n\n  buffer_construct(\u0026b);\n  buffer_load(\u0026b, \"/etc/motd\");\n  buffer_prepend(\u0026b, data_string(\"--- first line ---\\n\"));\n  buffer_append(\u0026b, data_string(\"--- last line ---\\n\"));\n  buffer_save(\u0026b, \"/tmp/motd\");\n  buffer_destruct(\u0026b);\n}\n```\n\n### Lists\n\nLists are doubly linked sequence containers, similar to C++ std::list, with O(1) inserts (given a known position) and deletes.\n\n#### Example\n\nNaive example printing primes up to n (10000).\n\n```C\nint main()\n{\n  list_t l;\n  int i, n = 10000, *p_first, *p, *p_next;\n\n  list_construct(\u0026l);\n  /* add all integers to n */\n  for (i = 1; i \u003c n; i++)\n    list_push_back(\u0026l, \u0026i, sizeof i);\n  /* skip first integer 1 */\n  p_first = list_next(list_front(\u0026l));\n  /* remove all multiples of first prime in list */\n  while (p_first != list_end(\u0026l) \u0026\u0026 *p_first \u003c= n / 2)\n  {\n    p = list_next(p_first);\n    while (p != list_end(\u0026l))\n    {\n      p_next = list_next(p);\n      if (*p % *p_first == 0)\n        list_erase(p, NULL);\n      p = p_next;\n    }\n    p_first = list_next(p_first);\n  }\n  /* print remaining integers */\n  list_foreach(\u0026l, p)\n    printf(\"%d\\n\", *p);\n  list_destruct(\u0026l, NULL);\n}\n```\n\n### Vectors\n\nVectors are dynamically resized arrays, similar to C++ std::vector, with O(1) random access, and O(1) inserts and removals at the end.\n\n#### Example\n\nCreate a vector with the first 50 Fibonacci integers.\n\n```C\nvoid fib(vector_t *v, size_t a, size_t b, int remaining)\n{\n  if (!remaining)\n    return;\n  vector_push_back(v, \u0026a);\n  fib(v, b, a + b, remaining - 1);\n}\n\nint main()\n{\n  vector_t v;\n  int i;\n\n  vector_construct(\u0026v, sizeof (size_t));\n  fib(\u0026v, 0, 1, 50);\n  for (i = 0; i \u003c vector_size(\u0026v); i++)\n    printf(\"%lu\\n\", *(size_t *) vector_at(\u0026v, i));\n  vector_destruct(\u0026v, NULL);\n}\n```\n\n### Maps\n\nMaps are associative containers that contains key-value pairs with unique keys, similar to std::unordered_map, with average O(1) random access, inserts, and removals.\n\nMaps come in a generic map_t flavour, as well as mapi_t for integer keys and maps_t for string keys.\n\n#### Example\n\nConstant lookups against a Fibonacci table.\n\n```C\nvoid fib(mapi_t *m, size_t a, size_t b, int remaining)\n{\n  if (!remaining)\n    return;\n  mapi_insert(m, a, 1, NULL);\n  fib(m, b, a + b, remaining - 1);\n}\n\nint main()\n{\n  mapi_t m;\n  size_t test[] = {233, 234, 377, 378, 610, 611};\n  int i;\n\n  mapi_construct(\u0026m);\n  fib(\u0026m, 0, 1, 50);\n  for (i = 0; i \u003c sizeof test / sizeof *test; i++)\n    printf(\"%lu %s a Fibonacci number\\n\", test[i], mapi_at(\u0026m, test[i]) ? \"is\" : \"is not\");\n  mapi_destruct(\u0026m, NULL);\n}\n```\n\n### UTF-8 strings\n\nUTF-8 compliant strings store an explicit string length to avoid zero termination issues.\n\n#### Example\n\nPrints a simple unicode string including the zero character.\n\n```C\nint main()\n{\n  string_t s = string_utf8_decode(\"unicode character \\\\ud83d\\\\ude03, zero character \\\\u0000, some more text...\\\\n\", NULL);\n\n  printf(\"string length: %lu\\n\", string_utf8_length(s));\n  fwrite(string_base(s), string_size(s), 1, stdout);\n}\n```\n\n### JSON\n\nJSON support includes an opaque complex value_t container that can dynamic generic data structures runtime, as well as serialize to, and deserialize from fully compliant JSON notation. The value_t type is extendable to support non standard JSON values such as binary data and references. \n\n#### Example\n\nCreates and serializes a simple value_t object.\n\n```C\nint main()\n{\n  value_t *v, *k;\n  string_t s;\n\n  v = value_object();\n  value_object_set_release(v, string(\"a smiley\"), value_string(string(\"\\U0001F600\")));\n  k = value_array();\n  value_object_set_release(v, string(\"some interesting numbers\"), k);\n  value_array_append_release(k, value_number(184467440737095516.0L));\n  value_array_append_release(k, value_number(123.456e-789L));\n  value_array_append_release(k, value_number(3.14L));\n\n  s = value_encode(v, 1);\n  fwrite(string_base(s), string_size(s), 1, stdout);\n  string_release(s);\n\n  value_release(v);\n}\n```\n\n### Memory pools\n\nMemory pools improves performance when frequently allocating and deallocating objects of a predetermined size.\n\n#### Example\n\nAllocates an integer and returns it to the pool.\n\n```C\nint main()\n{\n  pool_t p;\n  int *x;\n\n  pool_construct(\u0026p, sizeof (int));\n  x = pool_malloc(\u0026p);\n  *x = 42;\n  pool_free(\u0026p, x);\n  pool_destruct(\u0026p);\n}\n```\n\n### Events\n\nEvent driven architecture is a core design pattern of a libreactor application and implies using asyncronous operations to avoid blocking.\n\nThe simplest possible (do nothing) application is shown below. It will initialize the reactor, process all events, and then release all reasources required for the reactor. Since there is nothing triggering events it will terminate without side expressions.\n\n```C\nint main()\n{\n  reactor_construct();\n  reactor_loop();\n  reactor_destruct();\n}\n```\n\nAn event is a value created asyncronously by a producer, for example as a result of a asyncronous kernel syscall, or a high order abstract object.\n\nTo define how to receive events a *callback* and a *state* is typically prefixed to a low level syscall, or as arguments when constructing an object. The *state* and *data* are sent as part of the resulting *event* (`event-\u003estate` and `event-\u003edata`). Below is a simple application that asyncronously will read from *stdin* into a *buffer*. When the *reactor_read* command is completed the loop will exit.\n\n```C\nvoid callback(reactor_event_t *event)\n{\n  printf(\"read returned %d\\n\", (int) event-\u003edata);\n}\n\nint main()\n{\n  char buffer[1024];\n\n  reactor_construct();\n  reactor_read(callback, NULL, 0, buffer, sizeof buffer, 0);\n  reactor_loop();\n  reactor_destruct();\n}\n```\n\nThe power of the event driven pattern is that actors can operate concurrently in the same thread, with a shared state, without a need for locks, mutexes and semaphores, and without a need for context switching. This allows for highly scalable and performance optimized applications, while also minimizing the risk for race conditions. \n\nEvent driven applications tend to be `flexible, loosely-coupled and scalable`, `easier to develop and amenable to change` and `significantly more tolerant of failure`.\n\n### Threads\n\nThreads can be spawned and results collected using an event driven pattern.\n\n#### Example\n\n```C\nvoid async_callback(reactor_event_t *event)\n{\n  if (event-\u003etype == REACTOR_CALL)\n  {\n    printf(\"separate thread that can block and manipulate state\\n\");\n    sleep(1);\n    printf(\"exit\\n\");\n  }\n}\n\nint main()\n{\n  reactor_construct();\n  reactor_async(async_callback, NULL);\n  reactor_loop();\n  reactor_destruct();\n}\n```\n\n### IO Uring\n\nlibreactor is built directly on top of the io_uring asynchronous system call interface which is quite complex to work with. Using [liburing](https://github.com/axboe/liburing) simplifies the task somewhat but using it to build large applications is still cumbersome. libreactor presents a streamlined and very simple interface to work with.\n\nBesides the usual event loop construction, libreactor defines a simple function call for each io_uring syscall, prefixed with *callback* and *state* variables that are used when handling the result.\n\nInstead of the syncronous *connect()* call below\n\n```\nfd = connect(sock, \u0026sa, sizeof sa);\n```\n\nThe asyncronous *reactor_connect()* version is\n\n```\nid = reactor_connect(callback, state, sock, \u0026sa, sizeof sa);\n```\n\n*id* is an identifier for the asyncronous syscall and can be used to abort the operation.\n\nThe same pattern is used for all io_uring syscalls. For a a playful example look at [shufflecat](example/shufflecat.c) that asyncronously opens N files, and concurrently streams from all files to stdout, one byte at the time. All operations are non-blocking and concurrent in the same thread.\n\n### Signals\n\nPolled signals sent between actors or threads, based on the eventfd interface.\n\n#### Example\n\nSpawns a separate thread and waits for a signal.\n\n\n```C\nvoid callback(reactor_event_t *event)\n{\n  signal_t *signal = event-\u003estate;\n\n  printf(\"signal received\\n\");\n  signal_close(signal);\n}\n\nvoid producer(reactor_event_t *event)\n{\n  signal_t *signal = event-\u003estate;\n\n  if (event-\u003etype == REACTOR_CALL)\n    signal_send_sync(signal);\n}\n\nint main()\n{\n  signal_t signal;\n\n  reactor_construct();\n  signal_construct(\u0026signal, callback, \u0026signal);\n  signal_open(\u0026signal);\n  reactor_async(producer, \u0026signal);\n  reactor_loop();\n  signal_destruct(\u0026signal);\n  reactor_destruct();\n}\n```\n\n### Timers\n\nTimers triggers alerts at a set point in time and optionally continuously with a delay.\n\n#### Example\n\nTrigger an alert a second (1000000000ns) from now.\n\n```C\nvoid callback(__attribute__((unused)) reactor_event_t *event)\n{\n  printf(\"timeout\\n\");\n}\n\nint main()\n{\n  timeout_t t;\n\n  reactor_construct();\n  timeout_construct(\u0026t, callback, \u0026t);\n  timeout_set(\u0026t, reactor_now() + 1000000000, 0);\n  reactor_loop();\n  reactor_destruct();\n}\n```\n\n### Notify\n\nMonitoring filesystem events based on *inotify*.\n\n#### Example\n\nDisplays the first event for each file given as argument to the program.\n\n```C\nvoid callback(reactor_event_t *event)\n{\n  notify_t *notify = event-\u003estate;\n  notify_event_t *e = (notify_event_t *) event-\u003edata;\n\n  fprintf(stdout, \"%d:%04x:%s:%s\\n\", e-\u003eid, e-\u003emask, e-\u003epath, e-\u003ename);\n  notify_remove_path(notify, e-\u003epath);\n}\n\nint main(int argc, char **argv)\n{\n  notify_t notify;\n  int i, e;\n\n  reactor_construct();\n  notify_construct(\u0026notify, callback, \u0026notify);\n  notify_open(\u0026notify);\n  for (i = 1; i \u003c argc; i++)\n  {\n    e = notify_add(\u0026notify, argv[i], IN_ALL_EVENTS);\n    if (e == -1)\n      err(1, \"notify_add\");\n  }\n  reactor_loop();\n  notify_destruct(\u0026notify);\n  reactor_destruct();\n}\n```\n\n### Streams\n\nBuffered streams with read/write/flush semantics on top of file descriptors and sockets.\n\n#### Example\n\nStream from stdin.\n\n```C\nstatic void input(reactor_event_t *event)\n{\n  stream_t *stream = event-\u003estate;\n  data_t data;\n\n  switch (event-\u003etype)\n  {\n  case STREAM_READ:\n    data = stream_read(stream);\n    stream_consume(stream, data_size(data));\n    fwrite(data_base(data), data_size(data), 1, stdout);\n    break;\n  case STREAM_CLOSE:\n    stream_destruct(stream);\n    break;\n  }\n}\n\nint main()\n{\n  stream_t s;\n\n  reactor_construct();\n  stream_construct(\u0026s, input, \u0026s);\n  stream_open(\u0026s, 0, 0);\n  reactor_loop();\n  reactor_destruct();\n}\n```\n\n### Servers\n\nSimple network server routines.\n\n#### Example\n\nCreate 100 concurrent TCP servers on ports 10000 to 10099. \n\n```C\n void callback(reactor_event_t *event)\n{\n  int fd;\n\n  if (event-\u003etype == NETWORK_ACCEPT)\n  {\n    fd = event-\u003edata;\n    printf(\"connection on fd %d\\n\", fd);\n    reactor_close(NULL, NULL, fd);\n  }\n}\n\nint main(int argc, char **argv)\n{\n  int i;\n\n  reactor_construct();\n  for (i = 10000; i \u003c 10100; i ++)\n    network_accept(callback, NULL, NULL, i, NETWORK_REUSEADDR);\n  reactor_loop();\n  reactor_destruct();\n}\n```\n\n### Clients\n\nSimple network client routines.\n\n#### Example\n\nScan 50 most common TCP ports (For a slightly more advanced version checkout [scan.c](example/scan.c)).\n\n```C\nconst int top50[] = {\n  21,22,23,25,26,53,80,81,110,111,113,135,139,143,179,199,443,445,465,514,515,\n  548,554,587,646,993,995,1025,1026,1027,1433,1720,1723,2000,2001,3306,3389,\n  5060,5666,5900,6001,8000,8008,8080,8443,8888,10000,32768,49152,49154\n};\n\nstatic void callback(reactor_event_t *event)\n{\n  if (event-\u003etype == NETWORK_CONNECT)\n    printf(\" %d\", *(int *) event-\u003estate);\n}\n\nint main(int argc, char **argv)\n{\n  int i, j;\n\n  reactor_construct();\n  for (i = 1; i \u003c argc; i ++)\n  {\n    printf(\"[%s]\", argv[i]);\n    for (j = 0; j \u003c sizeof top50 / sizeof *top50; j++)\n      network_connect(callback, (void *) \u0026top50[j], argv[i], top50[j]);\n    reactor_loop();\n    printf(\"\\n\");\n  }\n  reactor_destruct();\n}\n```\n\n### Resolvers\n\nNetwork address and service translation. The resolver will cache results for 10 seconds (10^10ns), configurable with ```network_expire()```. Identical requests will be consolidated and queued to avoid thundering herd issues.\n\n#### Example\n\nLookup the IP address for www.google.com.\n\n```C\nvoid callback(reactor_event_t *event)\n{\n  struct addrinfo *ai = (struct addrinfo *) event-\u003edata;\n  char host[NI_MAXHOST];\n\n  while (ai)\n    {\n      getnameinfo(ai-\u003eai_addr, ai-\u003eai_addrlen, host, sizeof host, NULL, 0, NI_NUMERICHOST);\n      printf(\"%s\\n\", host);\n      ai = ai-\u003eai_next;\n    }\n}\n\nint main(int argc, char **argv)\n{\n  reactor_construct();\n  network_resolve(callback, NULL, \"www.google.com\");\n  reactor_loop();\n  reactor_destruct();\n}\n```\n\n### HTTP\n\nServer framework for creating webservers and API services.\n\n#### Example\n\nSimple HTTP server.\n\n```C\nstatic void handle_request(reactor_event_t *event)\n{\n  server_plain((server_session_t *) event-\u003edata, string(\"hello\"), NULL, 0);\n}\n\nint main()\n{\n  server_t server;\n\n  reactor_construct();\n  server_construct(\u0026server, handle_request, NULL);\n  server_open(\u0026server, \"127.0.0.1\", 80);\n  reactor_loop();\n  reactor_destruct();\n}\n```\n\n### Queues\n\nMessage queues suitable for communication between different reactor threads.\n\n#### Example\n\nSingle-producer single-consumer example.\n\n```C\nstatic void sender(reactor_event_t *event)\n{\n  queue_t *queue = event-\u003estate;\n  queue_producer_t producer;\n  int i;\n\n  if (event-\u003etype == REACTOR_RETURN)\n    return;\n\n  reactor_construct();\n  queue_producer_construct(\u0026producer);\n  queue_producer_open(\u0026producer, queue);\n  for (i = 10; i \u003e= 0; i--)\n    queue_producer_push(\u0026producer, (int []){i});\n  reactor_loop();\n  queue_producer_destruct(\u0026producer);\n  reactor_destruct();\n}\n\nstatic void receiver_event(reactor_event_t *event)\n{\n  int *element = (int *) event-\u003edata;\n\n  printf(\"%d\\n\", *element);\n  if (*element == 0)\n    queue_consumer_close(event-\u003estate);\n}\n\nstatic void receiver(reactor_event_t *event)\n{\n  queue_t *queue = event-\u003estate;\n  queue_consumer_t consumer;\n\n  if (event-\u003etype == REACTOR_RETURN)\n    return;\n\n  reactor_construct();\n  queue_consumer_construct(\u0026consumer, receiver_event, \u0026consumer);\n  queue_consumer_open(\u0026consumer, queue, 1);\n  queue_consumer_pop(\u0026consumer);\n  reactor_loop();\n  queue_consumer_destruct(\u0026consumer);\n  reactor_destruct();\n}\n\nint main()\n{\n  queue_t queue;\n\n  reactor_construct();\n  queue_construct(\u0026queue, sizeof(int));\n  reactor_async(sender, \u0026queue);\n  reactor_async(receiver, \u0026queue);\n  reactor_loop();\n  queue_destruct(\u0026queue);\n  reactor_destruct();\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffredrikwidlund%2Flibreactorng","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffredrikwidlund%2Flibreactorng","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffredrikwidlund%2Flibreactorng/lists"}