ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
ra8_fs_fat_lock.c File Reference

The optional mutual-exclusion seam taken around every public call. More...

#include <stddef.h>
#include <stdint.h>
#include "ra8_attributes.h"
#include "ra8_fs.h"
#include "ra8_fs_fat_internal.h"
Include dependency graph for ra8_fs_fat_lock.c:

Go to the source code of this file.

Functions

void priv_lock_acquire (void)
 Take the library lock, if the caller installed one.
void priv_lock_release (void)
 Drop the library lock taken by priv_lock_acquire.
ra8_err_t ra8_fs_set_lock (const ra8_fs_lock_t *lock)
 Install (or remove) the lock taken around every public ra8_fs call.

Variables

static ra8_fs_lock_t s_lock
 The installed lock binding; meaningful only when s_lock_installed.
static bool s_lock_installed
 True once a complete binding has been installed.

Detailed Description

The optional mutual-exclusion seam taken around every public call.

ra8_fs was written for a world with no scheduler, and its shared state – the file-handle table, the mount table, the one static scratch sector – is serialised by there being nobody else to race. That is the correct default and it stays the default: with no binding installed the two helpers here compile down to a load and a branch, and nothing else in the library changes.

What this file adds is somewhere for an RTOS-world caller to put its mutex, so the alternative is not "wrap every call site by hand" or "ship a second filesystem to get a lock". The binding is a pair of function pointers plus a cookie (ra8_fs_lock_t); no lock primitive, no RTOS header and no scheduler concept appears in this library, and the adapter that binds tx_mutex_get / tx_mutex_put lives with the caller that owns the mutex.

The lock is taken by the public entry points only. Every one of them is a wrapper of the shape

priv_lock_acquire(); e = priv_<op>_locked(...); priv_lock_release();

so a return path that forgets to release cannot be written – there is one return per wrapper – and every guarded implementation carries RA8_EXPECTS_LOCK("ra8_fs_lock") so the annotation checker, not a comment, is what says no internal helper may take it a second time.

Since
0.1.0

Definition in file ra8_fs_fat_lock.c.

Function Documentation

◆ priv_lock_acquire()

void priv_lock_acquire ( void )

Take the library lock, if the caller installed one.

Invokes the installed ra8_fs_lock_t::acquire with its cookie. With no binding installed – the bare-metal default – this is a load and a branch and nothing else, which is why the seam costs the default world nothing. Called only by the public entry-point wrappers: an internal helper taking it a second time would deadlock a non-recursive mutex, and the RA8_EXPECTS_LOCK("ra8_fs_lock") tag on every guarded implementation is what enforces that.

Returns
Nothing.
Precondition
The calling thread does not already hold the lock.
The installed binding (if any) is complete – guaranteed by ra8_fs_set_lock, which rejects a half-filled one.
Postcondition
The lock is held, or no binding is installed.
No library state other than the caller's lock is touched.
Note
Pairs 1:1 with priv_lock_release on every return path.
Since
0.1.0

Definition at line 75 of file ra8_fs_fat_lock.c.

References s_lock, and s_lock_installed.

Referenced by ra8_fs_check(), ra8_fs_close(), ra8_fs_dir_next(), ra8_fs_dir_open(), ra8_fs_format(), ra8_fs_free_space(), ra8_fs_get_label(), ra8_fs_listdir(), ra8_fs_mkdir(), ra8_fs_mount(), ra8_fs_mount_partition(), ra8_fs_open(), ra8_fs_probe(), ra8_fs_read(), ra8_fs_rename(), ra8_fs_rmdir(), ra8_fs_seek(), ra8_fs_set_attr(), ra8_fs_set_label(), ra8_fs_size(), ra8_fs_stat(), ra8_fs_tell(), ra8_fs_truncate(), ra8_fs_unlink(), ra8_fs_unmount(), ra8_fs_utime(), ra8_fs_write(), and ra8_fs_write_file().

◆ priv_lock_release()

void priv_lock_release ( void )

Drop the library lock taken by priv_lock_acquire.

Invokes the installed ra8_fs_lock_t::release with its cookie, or does nothing when no binding is installed. This is the release half the annotation checker looks for when it decides whether a public wrapper discharged the ownership it took.

Returns
Nothing.
Precondition
A matching priv_lock_acquire ran on this thread.
The binding has not changed since that call.
Postcondition
The lock is no longer held.
No library state other than the caller's lock is touched.
Note
Called on the success path and every error path of each wrapper.
Since
0.1.0

Definition at line 84 of file ra8_fs_fat_lock.c.

References s_lock, and s_lock_installed.

Referenced by ra8_fs_check(), ra8_fs_close(), ra8_fs_dir_next(), ra8_fs_dir_open(), ra8_fs_format(), ra8_fs_free_space(), ra8_fs_get_label(), ra8_fs_listdir(), ra8_fs_mkdir(), ra8_fs_mount(), ra8_fs_mount_partition(), ra8_fs_open(), ra8_fs_probe(), ra8_fs_read(), ra8_fs_rename(), ra8_fs_rmdir(), ra8_fs_seek(), ra8_fs_set_attr(), ra8_fs_set_label(), ra8_fs_size(), ra8_fs_stat(), ra8_fs_tell(), ra8_fs_truncate(), ra8_fs_unlink(), ra8_fs_unmount(), ra8_fs_utime(), ra8_fs_write(), and ra8_fs_write_file().

◆ ra8_fs_set_lock()

ra8_err_t ra8_fs_set_lock ( const ra8_fs_lock_t * lock)
nodiscard

Install (or remove) the lock taken around every public ra8_fs call.

There is one lock for the whole library, not one per mount: the state that needs serialising – the file-handle table, the mount table and the static scratch sector – is shared by every mount. Installing a lock therefore serialises every public entry point against every other one.

Passing NULL removes the binding and restores the default: no lock, no call, and the caller is once again responsible for serialising access. That is the state the library powers up in, so a bare-metal consumer never calls this function at all and pays nothing for its existence.

The binding is copied, so the caller's ra8_fs_lock_t need not outlive the call; the object ra8_fs_lock_t::ctx points at must, however, outlive every subsequent ra8_fs call.

Call this before the first ra8_fs call and before any other thread can reach the library: swapping the binding while a call is in flight would release a lock the caller never took.

Parameters
[in]lockLock binding to install, or NULL to remove the current one.
Returns
ra8_err_t Error code.
Return values
k_ra8_okBinding installed (or removed for a NULL lock).
k_ra8_err_invalid_arglock is non-NULL but acquire or release is NULL – a half-filled binding would leave the library holding a lock it cannot drop.
Precondition
No ra8_fs call is in flight on any thread.
When lock is non-NULL, both of its callbacks are non-NULL.
Postcondition
On k_ra8_ok with a non-NULL lock, every subsequent public call brackets itself with acquire / release.
On k_ra8_ok with a NULL lock, no callback is invoked again.
On k_ra8_err_invalid_arg the previous binding is unchanged.
Note
Not thread-safe with respect to itself; this is an init-time call.
Warning
Installing a lock does not make one open ra8_fs_file_t shareable between threads. It serialises the library, not a handle: two threads reading the same handle still race on its offset.
MC/DC:
Decision: if (lock->acquire == nullptr || lock->release == nullptr) – vectors live in tests/storage/src/test_ra8_fs_lock.c.
See also
ra8_fs_lock_t The binding this installs.
Since
0.1.0

Definition at line 92 of file ra8_fs_fat_lock.c.

References ra8_fs_lock_t::acquire, k_ra8_err_invalid_arg, k_ra8_ok, ra8_fs_lock_t::release, s_lock, and s_lock_installed.

Referenced by tx_application_define().

Variable Documentation

◆ s_lock

ra8_fs_lock_t s_lock
static

The installed lock binding; meaningful only when s_lock_installed.

A copy of the caller's ra8_fs_lock_t rather than a pointer to it, so a binding built as a compound literal or on the installing function's stack stays valid for the life of the program. The ctx cookie is still the caller's to keep alive.

Note
Written only by ra8_fs_set_lock, which is an init-time call.
Warning
Never modify directly; a half-updated binding would leave the library able to take a lock it cannot drop.
Since
0.1.0

Definition at line 58 of file ra8_fs_fat_lock.c.

Referenced by priv_lock_acquire(), priv_lock_release(), and ra8_fs_set_lock().

◆ s_lock_installed

bool s_lock_installed
static

True once a complete binding has been installed.

Kept separate from s_lock so "no lock" is one predictable branch rather than a NULL test on a function pointer that the compiler must reload.

Note
Written only by ra8_fs_set_lock.
Warning
Never modify directly.
Since
0.1.0

Definition at line 72 of file ra8_fs_fat_lock.c.

Referenced by priv_lock_acquire(), priv_lock_release(), and ra8_fs_set_lock().