[HN Gopher] Show HN: Async tasks in 350 lines of C
       ___________________________________________________________________
        
       Show HN: Async tasks in 350 lines of C
        
       Tasks are a primitive for asynchronous programming and an
       alternative to working with threads directly. Instead of creating a
       thread, performing work, then waiting on it using synchronization
       primitives, you only define a unit of work and let a scheduler
       decide when and where to execute it. The threads that the scheduler
       uses are created once and reused for many tasks throughout the
       program's lifetime. In most cases, creating and executing a task
       requires not even a single syscall and is a significantly faster
       operation than creating a thread, resulting in lower latency.
       Furthermore, tasks from different subsystems can be automatically
       interleaved by the scheduler, which is difficult to achieve
       manually with threads without communication between the subsystems.
       The library is written in standard C11 and only depends on the C
       POSIX library. It is designed to be easy to integrate into a
       project by just copying the header and simple enough to be
       understood in an afternoon.
        
       Author : rkaehn
       Score  : 76 points
       Date   : 2024-03-11 12:44 UTC (10 hours ago)
        
 (HTM) web link (github.com)
 (TXT) w3m dump (github.com)
        
       | Cieric wrote:
       | I've actually been looking for something like this to translate
       | over to zig. Thanks for sharing! The only thing it seems to be
       | missing for my purposes is a way of returning a value.
        
         | rad_gruchalski wrote:
         | How would an asynchronous system return a value? This is a job
         | for something akin to a JS promise or golang channel.
        
           | LoganDark wrote:
           | An asynchronous task should be able to return a value if it's
           | 'awaited' by another asynchronous task.
        
             | rad_gruchalski wrote:
             | Yes. Using a promise/future/channel what have you.
        
               | LoganDark wrote:
               | Those are objects. It should be possible to nest tasks
               | without introducing object-oriented abstractions. If that
               | isn't possible with this library, then I agree with the
               | original commenter that it's kind of a missing feature.
        
           | Cieric wrote:
           | Yeah I'm seeing the sync functions, since those should wait
           | till the task is finished any returned value should be able
           | to be received at that point. Even a future promise system
           | should be possible since it's basically just a pointer to an
           | object with a flag saying if the object has been returned yet
           | or not.
           | 
           | I'm not going to press someone else to do it though, I'm just
           | gonna implement it myself since I have to rewrite it all
           | anyways.
        
         | o11c wrote:
         | Presumably you're supposed to include space for that as part of
         | the argument when you construct the task in the first place,
         | then just run until the task is complete. Given the awkwardness
         | of returning bigger-than-pointer types anyway, this is actually
         | easier than the alternative.
         | 
         | That said, the API here is undocumented and the chosen names
         | are very confusing (implementations do not do what I would
         | expect functions of those names to do).
        
       | marmaduke wrote:
       | Since performance was a consideration for you, how does it
       | compare to TBB or even say OpenMP? I've used those as well as the
       | task system that comes with ISPC for HPC stuff, but I like the
       | lightweight C11 approach here.
        
         | sakras wrote:
         | I'm almost certain this will be slower than OpenMP because it
         | uses a centralized task queue that gets locked. OpenMP uses a
         | decentralized work-stealing task queue called the Chase-Lev
         | Deque. There's a C implementation in this paper:
         | 
         | https://fzn.fr/readings/ppopp13.pdf
        
       | up2isomorphism wrote:
       | Posts like this realistically remind people that C won't go away
       | any time soon.
        
         | mm007emko wrote:
         | If no government bans it and mandates Linux kernel to be
         | rewritten in Rust (and all higher language FFIs to be
         | compatible with Rust rather than C), it's here to stay.
        
       | swingingFlyFish wrote:
       | God I love C. Thanks for reminding me.
        
       | kccqzy wrote:
       | How is this different from Apple's Grand Central Dispatch aka
       | libdispatch? Based on my superficial understanding they appear to
       | be quite similar.
        
         | krackers wrote:
         | Yeah to me they seem to fill a similar/same niche, both are
         | implementations of dispatch queues. cr_task_run_sync mapping to
         | dispatch_sync and cr_task_wait_request_signal + cr_task_run
         | mapping to dispatch_async. This library seems to be slightly
         | more low level in that by default you have to explicitly "run"
         | the task whereas with GCD blocks are scheduled and run as soon
         | as you enqueue them.
         | 
         | GCD is a lot more heavyweight though (but it brings a lot of
         | niceties), whereas this is going to be much more portable.
        
       | eqvinox wrote:
       | Purely out of curiosity -- the indentation style is very unusual.
       | Where did you pick it up?
        
         | colejohnson66 wrote:
         | It looks like nothing more than 1TBS[0] plus no line breaks
         | inside functions.
         | 
         | [0]:
         | https://softwareengineering.stackexchange.com/a/99546/54480
        
         | thenewwazoo wrote:
         | It looks like bog-standard K&R style[0] to me. Exceedingly
         | common, in my experience. What about it strikes you as odd?
         | 
         | [0] https://en.wikipedia.org/wiki/Indentation_style#K&R_style
        
         | antirez wrote:
         | That's effectively the most obvious indentation style used for
         | C.
        
       | hinkley wrote:
       | My first job our software ran on Windows, Linux and Mac. This was
       | well before OS X, and I was horrified to learn that all the
       | 'multitasking' in the Mac version was a message pump and longjmp
       | calls all-the-fuck-over. Decode a few blocks of gzip compressed
       | data, then check for network traffic. Then decode part of a JPEG.
       | I don't know how they did it.
       | 
       | The were, however, the only people in the world with multiple
       | monitors (outside of SGI).
        
       | j1elo wrote:
       | Oh boy, this reminds me of the good (not-so-)old times! Some
       | years ago I was writing embedded C for Atmel AVR32 chips, and
       | needed them to do several things "at the same time".
       | 
       | Started from learning about Protothreads (cooperative
       | concurrency) as described by Adam Dunkels [1], and ended up
       | devising a Task manager/runner library with a main loop, so
       | multiple protothreads could be scheduled easily. At any given
       | point in time, any of the scheduled tasks could be running or
       | yielding on some continuation point.
       | 
       | There's some beauty in grasping these concepts from the ground
       | up. Some time later I had to learn JavaScript, and the concept of
       | async/await clicked so fast and nicely in my mind. Saving
       | distances, the core idea is fundamentally the same, and that was
       | enlightening (and fun!)
       | 
       | [1]: https://dunkels.com/adam/pt/
        
       | dc-programmer wrote:
       | What do lines 7-21 do?
        
         | rkaehn wrote:
         | The library is an "stb-style" header-only library, so the
         | header has two modes: when you define CR_TASK_IMPL, it includes
         | the implementation (starting from line 20), otherwise it just
         | includes declarations (the first 19 lines). The implementation
         | should only be included in one translation unit, but the
         | declarations can be included in multiple units.
        
         | salamander014 wrote:
         | Historically, C compilers attempted to build the list of all
         | symbols in one pass of the files.
         | 
         | Sometimes, functions may call other functions in the same code
         | file.
         | 
         | This required that functions be declared before they are
         | referenced so C knew it existed.
         | 
         | You can also see this on lines 84-92.
        
       | lexicality wrote:
       | Huh, I didn't think the Obfuscated C Contest was open for
       | submissions yet
        
       ___________________________________________________________________
       (page generated 2024-03-11 23:01 UTC)