AmigaDOS Notification Requests¶
Notification requests let a program monitor file or directory changes made by other processes. When the filesystem detects a change to the monitored object, it either sends a NotifyMessage to a MsgPort, or sets a signal bit on a Task. Notification requests were added in V36; the RAM-Handler supported them from the start, the Fast File System from V37. Not all filesystems implement them — network filesystems (whose protocol lacks a change-notification primitive) typically do not.
Granularity rules:
- For a file: the request fires only after the modified file is closed, to avoid one notification per write.
- For a directory: add/remove files or links, and renames within the directory, fire the request.
- Changes to metadata (protection bits, comments) are filesystem-dependent — recent AmigaDOS versions treat them as modifications; older or third-party filesystems may not.
- Assign creation, volume removal, reinsertion, or inhibition of the volume holding the object do not fire the request.
- Issuing a request on a non-existent object in hope of being notified when it is created is not supported.
The canonical use case is IPrefs: it monitors preference files and reloads settings when the user saves a new version.
The NotifyRequest structure¶
Defined in dos/notify.h. Allocate with MEMF_CLEAR — some fields require zero-initialization, and once the request is issued the structure belongs to the filesystem and must not be touched.
struct NotifyRequest {
UBYTE *nr_Name; /* path of object to monitor, relative to cwd */
UBYTE *nr_FullName; /* filled in by dos.library; do not touch */
ULONG nr_UserData; /* free for caller; returned via NotifyMessage */
ULONG nr_Flags; /* NRF_SEND_MESSAGE / NRF_SEND_SIGNAL / NRF_NOTIFY_INITIAL / NRF_WAIT_REPLY */
union {
struct {
struct MsgPort *nr_Port; /* used with NRF_SEND_MESSAGE */
} nr_Msg;
struct {
struct Task *nr_Task; /* used with NRF_SEND_SIGNAL */
UBYTE nr_SignalNum;/* signal-bit *number*, not mask */
UBYTE nr_pad[3];
} nr_Signal;
} nr_stuff;
ULONG nr_Reserved[4]; /* must be zero */
ULONG nr_MsgCount; /* handler-private; do not touch */
struct MsgPort *nr_Handler; /* handler-private; do not touch */
};
Field-by-field:
nr_Name— path of the object to monitor, relative to the current directory.nr_FullName— written by the library to the full path; for handler use only.nr_UserData— opaque to the library; lets you tag a request so the recipient can identify which object fired (essential when many requests share onenr_Port).nr_Flags— see table below. Bits 16 and up are reserved for the filesystem; do not set them.nr_Port/nr_Task+nr_SignalNum— exactly one is used, selected by theNRF_SEND_*flag.nr_SignalNumis a bit number, not a mask.nr_Reserved[4]— must be zero.
Flags¶
| Flag | Purpose |
|---|---|
NRF_SEND_MESSAGE |
Send a NotifyMessage to nr_Port on change |
NRF_SEND_SIGNAL |
Set signal bit nr_SignalNum on nr_Task on change |
NRF_WAIT_REPLY |
Do not fire again until the previous message has been replied |
NRF_NOTIFY_INITIAL |
Fire once immediately on enqueueing the request |
NRF_SEND_MESSAGE and NRF_SEND_SIGNAL are mutually exclusive — exactly one of them must be set. Bits 16 and up are reserved for filesystem-internal use.
NRF_WAIT_REPLY is incompatible with NRF_SEND_SIGNAL (signals have no reply). With NRF_SEND_MESSAGE it prevents notifications from piling up at the recipient's port: the filesystem will not issue a second message until the first has been replied. If multiple changes occurred during the wait, a single follow-up message is generated.
NRF_NOTIFY_INITIAL rolls the initial state read into the same flow as the monitored updates — useful for "show me what's there, then watch for changes" code paths.
The NotifyMessage structure¶
When NRF_SEND_MESSAGE is set, the filesystem delivers this message to nr_Port:
struct NotifyMessage {
struct Message nm_ExecMessage; /* standard exec Message */
ULONG nm_Class; /* always NOTIFY_CLASS */
UWORD nm_Code; /* always NOTIFY_CODE */
struct NotifyRequest *nm_NReq; /* back-pointer to the request that fired */
ULONG nm_DoNotTouch; /* handler-private */
ULONG nm_DoNotTouch2; /* handler-private */
};
nm_NReq lets the recipient disambiguate which request fired when several share one port. The nm_DoNotTouch fields belong to the filesystem.
Intuition-port reuse. The
NotifyMessagelayout deliberately mirrorsIntuiMessage, so an IDCMPMsgPortof an intuition window can also receiveNotifyMessages — provided that window stays alive at least as long as the request is active. Do not mix IDCMP and non-IDCMP traffic on the same port without separate handling.
StartNotify — issuing a request¶
BOOL StartNotify(struct NotifyRequest *nr); /* V36 */
Returns non-zero on success (with IoErr() undefined), or zero with IoErr() set on failure.
Once issued, the NotifyRequest belongs to the filesystem until EndNotify() succeeds. The handler may read or modify the structure at any time.
EndNotify — canceling a request¶
void EndNotify(struct NotifyRequest *nr); /* V36 */
EndNotify() must be called only on requests that succeeded via StartNotify(). Calling it on a failed start is unsafe.
The filesystem will dequeue any NotifyMessages it sent but that the client has not yet replied. The caller is still responsible for replying any message it already dequeued from its own port before terminating the request.
After EndNotify() returns, the NotifyRequest is again available to the caller — it may be freed, or reused for a fresh request.
Worked example¶
struct NotifyRequest *nr;
struct MsgPort *port; /* reply port for the notification */
ULONG sigbit; /* signal bit for the notification, used here only as gate */
/* 1. Create a port. NotifyMessages are exec Messages — any MsgPort works. */
port = CreateMsgPort();
if (!port) { /* fail */ }
/* 2. Allocate the request with MEMF_CLEAR so reserved fields are zero. */
nr = AllocMem(sizeof(*nr), MEMF_CLEAR);
if (!nr) { DeleteMsgPort(port); /* fail */ }
/* 3. Fill in the request. */
nr->nr_Name = "SYS:Prefs/Env-archive/SYS/font.prefs";
nr->nr_UserData = (ULONG)my_cookie; /* identify the source */
nr->nr_Flags = NRF_SEND_MESSAGE | NRF_WAIT_REPLY;
nr->nr_stuff.nr_Msg.nr_Port = port;
/* 4. Issue. */
if (!StartNotify(nr)) {
LONG err = IoErr();
FreeMem(nr, sizeof(*nr));
DeleteMsgPort(port);
/* handle err */
}
/* 5. Wait for messages. */
for (;;) {
struct NotifyMessage *nm;
WaitPort(port); /* or Wait(SIGBREAKF_CTRL_C | (1L << port->mp_SigBit)) */
while ((nm = (struct NotifyMessage *)GetMsg(port)) != NULL) {
/* do work — preload, react, etc. */
ReplyMsg((struct Message *)nm); /* required for NRF_WAIT_REPLY semantics */
}
if (user_wants_to_quit) break;
}
/* 6. Tear down. */
EndNotify(nr);
FreeMem(nr, sizeof(*nr));
DeleteMsgPort(port);
See Also¶
Sources: Thomas Richter, "ROM Kernel Reference Manual: AmigaDOS" (First Edition, 2024), Chapter 7.5 — Notification Requests.
Raw: raw/rkm/rkm-dos-book.md
Updated: 2026-10-02