MTX_POOL(9)

πŸ“¦ version 15.1, in freebsd 14.5, freebsd 15.1, nixpkgs unstable

πŸ’… MTX_POOL(9) β€” the mutex pool page, bestie edition πŸŽ€

okay so this is the Kernel Developer's Manual entry for mtx_pool and friends. it lives in section 9, it's a FreeBSD thing, and honestly? it's giving "shared economy but for mutexes." let's get into it πŸ’–


✨ NAME ✨

mtx_pool, mtx_pool_alloc, mtx_pool_find, mtx_pool_lock, mtx_pool_lock_spin, mtx_pool_unlock, mtx_pool_unlock_spin, mtx_pool_create, mtx_pool_destroy β€” the mutex pool routines. that's the whole squad 🫢


πŸ¦‹ SYNOPSIS πŸ¦‹

#include <sys/param.h>
#include <sys/lock.h>
#include <sys/mutex.h>

struct mtx *
mtx_pool_alloc(struct mtx_pool *pool);

struct mtx *
mtx_pool_find(struct mtx_pool *pool, void *ptr);

void
mtx_pool_lock(struct mtx_pool *pool, void *ptr);

void
mtx_pool_lock_spin(struct mtx_pool *pool, void *ptr);

void
mtx_pool_unlock(struct mtx_pool *pool, void *ptr);

void
mtx_pool_unlock_spin(struct mtx_pool *pool, void *ptr);

struct mtx_pool *
mtx_pool_create(const char *mtx_name, int pool_size, int opts);

void
mtx_pool_destroy(struct mtx_pool **poolp);

🌸 DESCRIPTION 🌸

so mutex pools are designed to be used as short term leaf mutexes β€” like, the very last mutex you'd acquire before calling mtx_sleep(9). the vibe is: they all share one pool of mutexes. you can pick a mutex out of the pool based on a pointer you hand in (which may or may not point to anything valid, lol it literally doesn't have to be real), or you can just allocate an arbitrary shared mutex from the pool and hang onto the returned mutex pointer for later use. iconic, honestly.

the shared mutexes in the mtxpool_sleep mutex pool β€” which, btw, is created by default β€” are your standard, non-recursive, blockable mutexes, and should only be used in appropriate situations. the mutexes in the mtxpool_lockbuilder mutex pool are similar, except they're initialized with the MTX_NOWITNESS flag so that they may be used to build higher-level locks. and yeah, other mutex pools may be created that contain mutexes with different properties, like spin mutexes. choose your fighter πŸ’…

the caller can lock and unlock mutexes returned by the pool routines, but since the mutexes are shared, the caller should not attempt to destroy them or modify their characteristics. not your mutex, not your business, periodt.

⚠️ okay so while pool mutexes are normally leaf mutexes (meaning you cannot depend on any ordering guarantees after obtaining one), you can still obtain other mutexes under carefully controlled circumstances. specifically: if you have a private mutex (one that was allocated and initialized by the caller), you can obtain it after obtaining a pool mutex if ordering issues are carefully accounted for. in these cases the private mutex winds up being the true leaf mutex. careful bestie πŸ™ˆ

πŸ’œ the advantages (the slay list)

  1. no structural overhead; i.e., they can be associated with a structure without adding bloat to it.
  2. mutexes can be obtained for invalid pointers, which is useful when one uses mutexes to interlock destructor operations.
  3. no initialization or destruction overhead.
  4. can be used with mtx_sleep(9).

😭 the disadvantages (the tradeoffs, ngl)

  1. should generally only be used as leaf mutexes.
  2. pool/pool dependency ordering cannot be guaranteed.
  3. possible L1 cache mastership contention between CPUs.

⭐ the actual functions ⭐

  • mtx_pool_alloc() β€” obtains a shared mutex from the specified pool. this routine uses a simple rover to choose one of the shared mutexes managed by the mtx_pool subsystem.
  • mtx_pool_find() β€” returns the shared mutex associated with the specified address. it'll create a hash out of the pointer passed into it and will choose a shared mutex from the specified pool based on that hash. and again: the pointer does not need to point to anything real. fake it till you make it ✨
  • mtx_pool_lock(), mtx_pool_lock_spin(), mtx_pool_unlock(), mtx_pool_unlock_spin() β€” lock and unlock the shared mutex from the specified pool associated with the specified address. they're literally a combination of mtx_pool_find() and mtx_lock(9), mtx_lock_spin(9), mtx_unlock(9), and mtx_unlock_spin(9), respectively. but heads up: since these routines must first find the mutex to operate on, they are not as fast as directly using the mutex pointer returned by a previous invocation of mtx_pool_find() or mtx_pool_alloc(). convenience tax πŸ’€
  • mtx_pool_create() β€” allocates and initializes a new mutex pool of the specified size. the pool size must be a power of two (no cap, that's the rule). the opts argument is passed to mtx_init(9) to set the options for each mutex in the pool.
  • mtx_pool_destroy() β€” calls mtx_destroy(9) on each mutex in the specified pool, deallocates the memory associated with the pool, and assigns NULL to the pool pointer. clean up your room, bestie 🧚

πŸ”₯ SEE ALSO πŸ”₯

locking(9), mutex(9)


πŸ‘‘ HISTORY πŸ‘‘

these routines first appeared in FreeBSD 5.0. vintage but still serving πŸ’…


and that's the whole page, bestie β€” no notes, ate and left no crumbs 🌈

this gem came from the Debian build of the Kernel Developer's Manual, dated February 6, 2010 β€” mwah, thank you for coming to my ted talk πŸŽ€

πŸ“¦ where this page comes from