Skip to content

Multi-node operation

Several copies of a door may run at the same time. OpenDoors reports the current node through od_control.od_node, but the application remains responsible for choosing node-specific temporary files and protecting shared application data.

Named reservations

OpenDoors provides portable, cooperative filesystem-backed reservations for resources shared by DOS, Windows, and Unix nodes. Configure one registry before initialization:

if(!od_reserve_configure("MYDOOR.SYN"))
    return 1;
od_init();

Every cooperating process must use the same registry path. At initialization, the process finds or appends a fixed-size record for its NodeX identity, or for the nodeless Local identity, and holds that record's lifetime lock. A later session with the same identity reuses its committed record. Because two processes may not legitimately use the same identity, failure to acquire that record's lifetime lock is a fatal initialization error rather than a reason to append a duplicate record.

Use a reservation name for each independently protected resource:

if(od_reserve_request("Users")
    && od_reserve_wait(20000) == OD_RESERVE_ACQUIRED)
{
    FILE *users = fopen("USERS.DAT", "r+b");
    if(users != NULL)
    {
        /* Read the current record, update it, flush it, then close it. */
        fclose(users);
    }
    od_reserve_end();
}

Requests with the same case-sensitive name are ordered using a bakery ticket. Different names do not block one another. Equal ticket values are expected and are ordered by the participants' stable record indices. A participant does not rewrite its ticket while that request remains queued. A timeout leaves the request queued, so call od_reserve_end() to cancel it.

The registry format is fixed-size and little-endian. Its header is immutable after creation. Each live participant writes only its own record payload and read-locks peer payloads; the registry lock serializes append and record-count snapshots. A new identity is allocated at physical end-of-file, while a later session of the same identity reclaims its previous committed record. An abandoned or partially written append slot becomes a permanent tombstone and is not adopted by another identity.

These guarantees are cooperative. All programs which access the protected application resource must request the same reservation name. The shared filesystem must propagate byte-range locks and file writes between hosts. On DOS, multi-node operation also requires a redirector or SHARE.EXE implementation which supports byte-range locking. If DOS reports that the record-locking function does not exist, OpenDoors treats reservations as uncontended so the same program still works on a non-shared, single-node DOS system. That fallback does not provide coordination with another process.

See od_reserve_configure(), od_reserve_request(), and od_reserve_wait() for the complete contracts.

Protect the complete update

Acquire the reservation before reading the data that will be modified. Keep it through the write, flush, and close. Reading first and reserving only for the write still permits two nodes to calculate updates from the same stale value.

Keep reserved intervals short. Do not wait for user input while holding one, and avoid unrelated display or file work. The ex_vote.c example uses the VoteUsers and VoteQuestions names so updates to the two files remain independent.

Node-specific configuration

Node numbers remain useful for log names, work directories, temporary protocol files, and locating per-node door-information files. They are not a substitute for reserving shared state.

If each node has a separate door-information directory, pass the appropriate path through the BBS command line or a per-node configuration. If all nodes share one OpenDoors configuration file, avoid putting node-specific connection settings in that shared file. The BBS, TASK environment variable, or od_parse_cmd_line() should supply the node identity instead.