AmigaDOS Record Locking¶
Whole-file locks (Ch. 6) control access to an entire filesystem object. Record locks control access to a contiguous region of an open file. They are advisory: the filesystem does not block reads or writes to a locked region — it only refuses to grant a second overlapping record lock whose mode conflicts with an existing one. All participating processes must therefore use the record-locking API; reading and writing the file proceed independently of locks.
Support is not universal. The FFS and the RAM-Handler implement record locks; many third-party handlers and most network filesystems do not. Programs that need portable behaviour should treat record locks as an opportunistic optimisation and be prepared for ERROR_ACTION_NOT_KNOWN from handlers that do not implement them.
Lock modes¶
Defined in dos/record.h:
| Mode | Timeout | Behaviour |
|---|---|---|
REC_EXCLUSIVE |
Honoured | Block up to timeout ticks (1/50 s); grant exclusive access to the region if available |
REC_EXCLUSIVE_IMMED |
Ignored | Fail immediately if the region is not exclusively available |
REC_SHARED |
Honoured | Block up to timeout ticks; grant shared access if available |
REC_SHARED_IMMED |
Ignored | Fail immediately if the region is not shared-available |
A shared lock is held against an exclusive lock, and vice versa. Multiple shared locks may overlap freely. Multiple exclusive locks may not overlap with anything — not even with each other.
A timeout of 0 makes REC_EXCLUSIVE/REC_SHARED behave like their _IMMED counterparts.
LockRecord — lock a single region¶
BOOL LockRecord(BPTR fh, ULONG offset, ULONG length, ULONG mode, ULONG timeout); /* V36 */
Attempts to lock [offset, offset+length) of the file behind fh. On success returns non-zero (with IoErr() in an undefined state — do not check it). On failure returns 0 with IoErr() = ERROR_LOCK_COLLISION (region conflicts with another lock) or ERROR_LOCK_TIMEOUT (timed out without _IMMED).
mode is one of the four constants above. timeout is in ticks (1/50 s); pass 0 for immediate failure.
UnLockRecord — release a single region¶
BOOL UnLockRecord(BPTR fh, ULONG offset, ULONG length); /* V36 */
Releases the matching region. The range passed in must match an existing lock exactly — partial unlock is not allowed and returns 0 with IoErr() = ERROR_UNLOCK_ERROR (or ERROR_RECORD_NOT_LOCKED, depending on the version).
IoErr() on success is again undefined.
LockRecords / UnLockRecords — atomic-ish batch locks¶
struct RecordLock {
BPTR rec_FH; /* filehandle (NULL terminates the array) */
ULONG rec_Offset;
ULONG rec_Length;
ULONG rec_Mode; /* REC_EXCLUSIVE / _IMMED / REC_SHARED / _IMMED */
};
BOOL LockRecords(struct RecordLock *array, ULONG timeout); /* V36 */
BOOL UnLockRecords(struct RecordLock *array); /* V36 */
LockRecords() walks the array sequentially, applying timeout to each non-immediate entry. The maximum time it blocks is therefore the sum of all timeouts (not timeout total — the autodoc is misleading).
Despite the name, it is not atomic. A second task can grab a conflicting lock between calls inside the same batch. To minimise this window, lock records in a consistent order across your program — typically ascending by rec_Offset.
If any element fails, all previously locked entries are released and the function returns 0. The error code from IoErr() is the last failure.
UnLockRecords() walks the array sequentially as well. If any unlock fails, it continues with the remaining entries and returns 0 without a reliably-set IoErr() — auditing after a batch unlock therefore requires tracking what you locked yourself.
Worked example: a multi-record exclusive update¶
#include <dos/dos.h>
#include <dos/record.h>
#define HDR_OFF 0
#define HDR_LEN 64
#define IDX_OFF 64
#define IDX_LEN 128
BOOL LockHeaderAndIndex(BPTR fh)
{
static struct RecordLock locks[] = {
{ 0, HDR_OFF, HDR_LEN, REC_EXCLUSIVE_IMMED, 0 },
{ 0, IDX_OFF, IDX_LEN, REC_EXCLUSIVE_IMMED, 0 },
{ 0, 0, 0, 0, 0 }, /* terminator */
};
locks[0].rec_FH = fh;
locks[1].rec_FH = fh;
/* LockRecords fills the rec_FH for the caller. */
return LockRecords(locks, 0); /* 0 = no waiting, equivalent to _IMMED */
}
void UnlockHeaderAndIndex(void)
{
static struct RecordLock locks[] = {
{ 0, HDR_OFF, HDR_LEN, 0, 0 },
{ 0, IDX_OFF, IDX_LEN, 0, 0 },
{ 0, 0, 0, 0, 0 },
};
UnLockRecords(locks);
}
Important constraints¶
- Record locks are tied to a specific open filehandle. Two opens of the same path produce two filehandles and the locks do not coordinate between them.
- A record lock is released when its filehandle is closed.
- File-handles opened with
MODE_NEWFILEare already exclusively locked on the whole file; record locks on such a handle are redundant but legal. - Locks on a region past the current end of file are permitted and govern future writes that extend into the region.
See Also¶
Sources: Thomas Richter, "ROM Kernel Reference Manual: AmigaDOS" (First Edition, 2024), Chapter 5.9 — Record Locking.
Raw: raw/rkm/rkm-dos-book.md
Updated: 2026-10-02