iv_work(3)

πŸ“¦
version 0.43.2 Β· 2 versions
  • 0.43.2 Β· alpine 3.22, alpine 3.23, alpine 3.24, debian trixie, epel 10, fedora 43, fedora 44, nixpkgs unstable, opensuse-leap 16.0, ubuntu noble, ubuntu questing, ubuntu resolute, ubuntu stonking (latest)
  • 0.42.4 Β· debian bookworm, epel 9, ubuntu jammy
, in alpine 3.22, alpine 3.23, alpine 3.24, debian trixie, epel 10, fedora 43, fedora 44, nixpkgs unstable, opensuse-leap 16.0, ubuntu noble, ubuntu questing, ubuntu resolute, ubuntu stonking

πŸ’– iv_work β€” ivykis worker thread management πŸ’–

okay bestie, so this is iv_work, part of the ivykis programmer's manual, and it's literally the page about making your threads do the heavy lifting so your event loop stays pretty and unbothered. 🧚✨

It's giving: the functions IV_WORK_POOL_INIT, iv_work_pool_create, iv_work_pool_put, IV_WORK_ITEM_INIT, iv_work_pool_submit_work, and iv_work_pool_submit_continuation. All of them. Together. As a girl gang. πŸ‘‘


✨ SYNOPSIS ✨

#include <iv_work.h>

struct iv_work_pool {
        int             max_threads;
        void            *cookie;
        void            (*thread_start)(void *cookie);
        void            (*thread_stop)(void *cookie);
};

struct iv_work_item {
        void            *cookie;
        void            (*work)(void *cookie);
        void            (*completion)(void *cookie);
};

void IV_WORK_POOL_INIT(struct iv_work_pool *this);
int iv_work_pool_create(struct iv_work_pool *this);
int iv_work_pool_put(struct iv_work_pool *this);
void IV_WORK_ITEM_INIT(struct iv_work_item *work);
int iv_work_pool_submit_work(struct iv_work_pool *this, struct
iv_work_item *work);
int iv_work_pool_submit_continuation(struct iv_work_pool *this, struct
iv_work_item *work);

like. look at that struct. two structs, six functions, one header. clean. minimal. iconic. πŸ’…


🌸 DESCRIPTION 🌸

🫢 making a pool

So here's the tea: you call iv_work_pool_create on a struct iv_work_pool object that you already initialised with IV_WORK_POOL_INIT, and boom β€” you've got yourself a pool of worker threads. ✨

What's it for? Two things, and they're both lowkey important:

  • offloading CPU intensive tasks to it, so those tasks don't go and negatively influence event handling latency in the calling thread (rude of them tbh)
  • enabling the use of multiple host CPUs for those CPU intensive tasks, because one core is not the moment

iv_work dynamically adjusts the number of threads in the pool to the amount of work there is to do. it's very self-aware, very adaptive, main character energy. πŸ¦‹

The ->max_threads member of struct iv_work_pool specifies the maximum number of threads that will be created in this pool. that's the ceiling, bestie. don't expect more.


πŸ’œ submitting work

Calling iv_work_pool_submit_work on a struct iv_work_item object previously initialised by IV_WORK_ITEM_INIT submits a work item to a pool. so cute, so efficient. πŸŽ€

Here's the breakdown:

  • ->work member of struct iv_work_item β€” the function that gets called in one of the worker threads in the pool specified by ->this, with ->cookie as its sole argument
  • when the work function has completed, iv_work will call the ->completion callback to indicate this β€” also with ->cookie as its sole argument β€” in the thread that iv_work_pool_create was called in for this pool object

so the work runs over there, the completion comes back here. it's giving boomerang. πŸͺƒ


🌈 continuations (the from-inside-a-worker special)

Calling iv_work_pool_submit_continuation from a worker thread allows submitting a work item similarly to iv_work_pool_submit_work. but here's the plot twist:

  • iv_work_pool_submit_work can only be called from the thread owning iv_work
  • iv_work_pool_submit_continuation can be called from any of the worker threads

The ->completion callback of these jobs will be executed from the thread owning iv_work. so it always comes home. 🏠


⭐ the NULL special case

As a special case, calling iv_work_pool_submit_work with a NULL work pool pointer will cause the work item to be processed in the local thread, from an iv_task(3) callback. no pool? no problem. still slays. πŸ”₯


🧚 thread_start and thread_stop

If the ->thread_start function pointer specified in struct iv_work_pool is not NULL, it will be called upon creation of a new worker thread, in the context of the created worker thread, with ->cookie as its sole argument.

⚠️ Calls to ->thread_start are not explicitly serialised, which should be kept in mind when manipulating state shared between threads from within that callback function. bestie please be careful, that's a race condition waiting to happen. πŸ’€

Similarly, if iv_work decides to terminate a worker thread β€” for example due to inactivity β€” ->thread_stop will be called in the context of the terminating thread, with ->cookie as its sole argument.

⚠️ Calls to ->thread_stop are also not explicitly serialised. same warning, same energy, same "please lock your stuff". πŸ™ˆ

πŸ“Œ the rules, the boundaries, the law

  • iv_work_pool_submit_work can only be called from the thread that iv_work_pool_create for this pool object was called in.
  • iv_work_pool_submit_continuation can called from any of the worker threads.
  • There is no way to cancel submitted work items. once it's submitted, it's submitted. no take-backs. periodt. πŸ’…
  • There is no guaranteed order, FIFO or otherwise, between different work items submitted to the same worker thread pool. it's giving chaos but make it scheduled. 😭

πŸ“ putting the pool down

When the user has no more work items to submit to the pool, its reference to the pool can be dropped by calling iv_work_pool_put.

And now the important bit, so listen up:

If there are still pending or running work items assigned to this pool when iv_work_pool_put is called, those work items will not be canceled. they will be allowed to run to completion, and their ->completion callbacks will be called as usual. 🫢

A similar thing holds for the ->thread_start and ->thread_stop callbacks β€” they can also still be called after iv_work_pool_put returns.

πŸ’€ Even so, the memory corresponding to the struct iv_work_pool can immediately be freed or reused by the user upon return of the iv_work_pool_put call. yes, immediately. yes, while those callbacks are still out there living their lives. mind the lifetime, bestie, it's the whole game.

πŸŽ€ internal tea

Internally, iv_work uses iv_thread(3) for its thread management. iykyk. πŸ‘‘


🌟 SEE ALSO 🌟


and that's the whole page, bestie!! πŸ’–βœ¨πŸŒΈ this one came from the ivykis manual, dated 2010-09-14, and it lives at section iv_work(3). go forth and thread responsibly πŸ¦‹πŸ’…πŸŒˆ