C-Index/II Home Page
|CI2 Home|Literature|Support|Documentation|Y2K|Order|License|


Chapter 6 Multi-user Functions

This chapter provides a general discussion of the multi-user capabilities of C-Index/II. It should be read before using the functions. A detailed description of each function is provided in the C-Index/II Reference Guide.

Features

C-Index/II fully supports the development of multi-user applications. The supplied source contains all the code for both single and multi-user access. The features of C-Index/II's multi-user capabilities are:

 

  • Applications can control the opening of files for exclusive use or shared use.
  • Applications can control the opening of files for read-write or read-only permission.
  • Both File and Record locking are allowed.
  • Data files are compatible for both single and multi-user access.
  • The source code is highly portable.
  • The multi-user features have been tested with many operating environments, including MSDOS Local Area Networks, OS/2, Windows NT and Unix.
  • A transportable semaphore system is included for more complex locking requirements.
  • All C-Index/II functions are supported in the multi-user mode.
  • A special scheme is provided which allows buffering in Shared Mode, called "Continuous Buffering".

Writing a multi-user system entails so many tasks that it is beyond the scope of this manual to give a complete tutorial on the subject. What we can do is provide guidelines for using the C-Index/II functions by setting out some rules for using the multi-user functions.

 

Multi-User Create/Open/Close Functions

bcreate(

CFILE *psp, /* file parameters */

char *name, /* file name to open */

int sharemode, /* EXCL or SHARED */

int formatmode, /* format mode flag */

NDXLIST *indexlist) /* ptr to index info */

 

int bopen(

CFILE *psp, /* file parameters */

char *name, /* file name to open */

int sharemode, /* EXCL or SHARED */

int readmode, /* read mode flag */

NDXLIST *indexlist) /* index type list */

 

dbcreate(

CFILE *psp, /* file parameters */

char *name, /* file name to open */

char *buf, /* work buffer address */

int len, /* work buffer length */

int sharemode, /* EXCL or SHARED */

int bytemode, /* format mode flag */

NDXLIST *indexlist) /* ptr to index info */

 

int dbopen(

CFILE *psp, /* file parameters */

char *name, /* file name to open */

char *buf, /* work buffer address */

int len, /* work buffer length */

int sharemode, /* EXCL or SHARED */

int readmode, /* read mode flag */

NDXLIST *indexlist) /* index type list */

 

mclose(psp)

CFILE *psp; /* file psp */

 

The following functions are replaced by the above functions and are supported for backwards compatibility: mcreate, dmcreate, mopen, dmopen, cclose, dclose, and dmclose.

Exclusive, Deny-Write and Shared Use

The functions bcreate, bopen, dbcreate, and dbopen allow specifying how files are opened for file sharing.

 

  • SHARED
In Shared use mode, multiple processes may access the file at the same time. C-Index/II automatically manages coordinating processing accessing the file at the same time.
  • EXCL
In the Exclusive use mode all other processes are locked out of the file. If the file is opened for exclusive use, the faster single-user operations for cnext and cprev will be used.
  • DENYWRITE
In Denywrite mode the process opening the file has exclusive permission to write to the file, however, other process may open the file using SHARED, provided that they specify CRDONLY for the read/write mode.

 

Read-Write and Read-Only Use

Files can be opened in one of two read/write modes. These control how the file is opened at the operating system level for reading only or reading and writing:

 

  • CRDONLY
Opens the operating system file using the read-only permission. This feature is supported by MSDOS, NT, and OS/2 Some operating systems do not support it, such as most variations of Unix.
  • CRDWRITE
Opens the operating system file using read and write permission.

 

Share-Bytes

The bopen, bcreate, dbopen and dbcreate functions attempt to locate a byte in the header that has not been locked. These bytes are referred to as "share-bytes". The first unlocked share-byte that is found becomes the "share-number" of that process for that file. The located share-byte is locked, and the share-number is marked in the psp for that file. The share-number is used for managing semaphores to determine the process that has set a semaphore in a file, and to determine when a process has terminated abnormally leaving record locks or semaphores set in the file.

Non-Concurrent File Reads

The default read sharing method for C-Index/II is "Non-Concurrent". In the shared mode, lock-out of the file is limited to the time that each individual read or write operation is doing its work. Each application process that tries to access the file must wait until the file is available using a "round robin" type of waiting algorithm. In practice this type of waiting is not noticeable. Only when a large number of file accesses are attempting to access the file at the same instant will the performance be noticeably slower. The alternative to Non-Concurrent reading is ReadShare (see below).

Concurrent File Reads: ReadShare

If C-Index/II has been compiled with the ReadShare feature enabled, the system handles reading in a more efficient manner. Multiple reading processes can access the file at the same time. Only when a writing process accesses the file are the readers blocked from access. Because of special programming requirements of ReadShare, it is not enabled by default. See Chapter7, Advanced Usage of C-Index/II, for more details.

Continuous Buffering

Local buffering for each process occurs at all times when in shared and exclusive-use modes. When a process requests information from the file in the shared-use mode, C-Index/II always reads the header record that contains an update number for the file (a long integer). This number is compared with the update number of the psp. If they are unequal, all buffers containing information for that file are cleared from that processes memory, and the request for a node proceeds by reading it from the disk.

In the event that very little updating of the file is occurring, the buffering system will be in full force, and an increased speed will result. If the file is being modified constantly, the buffers will essentially be ignored.

There is very little additional overhead to this scheme. The header record is always buffered in memory by the operating system, since it is accessed so often.

Record Locking

The record locking system consists of three types:

 

  • Locking the current record (for multi-key routines).
  • Locking the current entry in a specified file and index (for single-key routines).
  • Semaphore flags administered by the application (for single-key or multi-key routines).

 

In all cases the file system will never be corrupted as a result of multiple processes accessing the file. Record locking (i.e. locking a single-key entry in the index, or locking a record when using the multi-key routines) is the responsibility of the application program. When in shared use mode, entries cannot be deleted or changed when they have been locked by a sharing process. When using the current delete and update functions (cdelcur and cchgcur), the entry must have been locked before performing the operation.

When closing a file that has been opened in shared-use or deny-write modes, any outstanding record locks or semaphores for that process are removed. If a process should fail before closing the file, the system will automatically clear record locks and semaphores left outstanding from the failed process.

 

Semaphore Locking Functions

/* set semaphore */

msetsema(filepsp, key, rec, shareval)

CFILE *filepsp; /* semaphore file psp */

char *key; /* semaphore key value */

long rec; /* semaphore record number */

int shareval; /* share-byte process id */

 

/* clear semaphore */

mclrsema(filepsp, key, rec, shareval)

CFILE *filepsp; /* semaphore file psp */

char *key; /* semaphore key value */

long rec; /* semaphore record number */

int shareval; /* share-byte process id */

 

/* test for semaphore */

mtstsema(psp, key, rec, shareval)

CFILE *filepsp; /* semaphore file psp */

char *key; /* semaphore key value */

long rec; /* semaphore record number */

int shareval; /* share-byte process id */

 

The semaphore locking functions permit the application programmer to set up any arbitrary record locking system. Unlike operating system based semaphore systems, this file based approach to semaphores is fully transportable between operating systems. The semaphore functions actually use in a special manner the cdupadd, cdelete and cfind functions to set, clear and test semaphores. The semaphore entries are added to the dedicated semaphore index, SEMAINDEX (as defined in cndx.h).

 

The Semaphore Physical Entry

The semaphore is a special C-Index/II entry which consists of a key value, a record number and a data value.

The key value is a binary key consisting of three parts. The first part is a single byte indicating the length (excluding the length byte itself), just like any other binary key. The second part is a single byte value. The locking functions msetlock and msetrec use this value to indicate which index is being locked by this semaphore. It can also be any value set by the application when calling the msetsema function. In this case the value should be outside the range of legal index numbers (NUMINDXS). This will avoid conflict with functions which automatically call semaphore routines to clear locks.

The record number of a semaphore is a 4 byte long value. The locking functions msetlock and msetrec use this value to indicate the record number value of the entry or record that is being locked. It can be set to any value by applications creating their own semaphores.

The data portion of the semaphore is the share-byte number. This identifies which process has created the semaphore entry. This must be set to the value in psp->sharenum.

The semaphore system is very flexible because you can lock, unlock, or test for locked condition, any single record or even groups of records. The record or records may be passwords that are administered by the system, tasks, or programs that need queuing. You can control the access to any resource in a multi-user environment that is under application program control.

Semaphore Abnormal Termination

C-Index/II uses an automatic test procedure to prevent orphan semaphores in the file caused by abnormal termination of a process. If a matching semaphore is encountered in the file when trying to set or test a semaphore, the system automatically tests the share-byte of the semaphore in question (in the data part of the semaphore entry) to see if the locking process is still active. If the process is not still active (through an abnormal program termination in which the file was not closed), the unused semaphore is removed and the operation continues as if the semaphore had not been in the file. Otherwise, the semaphore is considered valid and a semaphore lock is indicated.

 

Single-Key Entry Locking Functions

/* lock current entry */

msetlock(filepsp, keyn)

CFILE *filepsp; /* key file psp */

int keyn; /* index to be locked */

 

/* unlock current entry */

mclrlock(filepsp, keyn)

CFILE *filepsp; /* key file psp */

int keyn; /* index to be locked */

 

/* test lock on current entry */

mtstlock(filepsp, keyn)

CFILE *filepsp; /* key file psp */

int keyn; /* index to be tested */

 

These functions control the locking of individual entries in the file. The entry that is locked must be the current entry (as set by cfind, cnext, cprev, etc). The system permits only a single current entry to be locked in each index of a file. If a current entry is locked and the current pointer is then moved (by cfind, cnext, cprev, cfirst, clast, cdelete, cdupadd or cunqadd), that entry is automatically unlocked.

In the msetlock function an error is returned if there is no current record (NOCUR), or if the entry is already locked by another process (RECSHLK).

The mtstlock function tests to see if another process or the program itself has already locked the entry. The mtstlock function returns RECSFLK if the entry is locked by the calling process (self locked), RECSHLK if it is locked by a sharing processing, and RECUNLK if the entry is unlocked.

When locking the current entry, the system automatically builds a semaphore from the current key information and adds the key to the semaphore index (SEMAINDEX) by using the msetsema function. The semaphore that is built includes the current index number, the current entry key value, the current entry record number, and the process share-number.

Application Notes

In addition to changing the opens and closes, you must add record locking. Specifically, records may not be modified or deleted using the current operations, cchgcur or cdelcur, until they have been locked using msetlock. Failure to lock the record will result in these functions returning a NOLOCK error.

Your application may use the non-current operations, cchange, cdelete, and csave to modify records without locking them first. However, if another process has locked the record when the operation is requested, an error, RECSHLK (record shared lock), will be returned and the operation will not be performed.

Multi-Key Record Locking Functions

/* lock current record */

msetrec(filepsp)

CFILE *filepsp; /* key file psp */

 

/* unlock current record */

mclrrec(filepsp)

CFILE *filepsp; /* key file psp */

 

/* test lock on current record */

mtstrec(filepsp)

CFILE *filepsp; /* key file psp */

 

These functions are the equivalent to the entry locking functions, except that they lock, unlock, and test locks on the current record that has been set by the multi-key routines (indicated by currec in the psp). The record may be locked after setting the psp.currec record number by using either the dfind or dseq functions, or by setting the psp.currec variable manually. The record will remain locked until it is explicitly unlocked using the mclrrec function, or until a new record is set using msetrec, or implicitly by performing a dread operation using a record number different than that used in the msetrec operation (thus changing the current record).

The typical usage would be to find a record using dfind or dseq, do the msetrec to lock it, perform updates on the record and then unlock the record with mclrrec.

When in the SHARED file mode, the current record must be locked using msetrec before ddelete will delete the record. This enforces the protection of records that may be locked by other processes.

Multi-User Error Codes

FILENOTLOCKED -101 File is not locked

FILELOCKED -102 File is locked

MODEERR -103 File mode error

RECSHLK -104 Rec locked by sharing process.

RECSFLK -105 Record is locked by self.

RECUNLK -106 Record is unlocked.

RECNOLK -107 Record is unlocked and should be locked.

RSTERR -108 Error resetting the share byte after extending the file.

USRERR -109 Too many users logged onto file.

BADQUE -110 Problem maintaining write queue.

 

Disabling Required Record Locking

When a file is opened in SHARED mode C-Index requires that records be locked before they can be updated or deleted (using ddelete, cdelete, dupdate, cchange, etc). This default requirement can be changed by setting the "nolock" variable in the parameter structure to TRUE after opening the file. When psp->nolock is TRUE, C-Index will allow any delete or update function to proceed without it being locked first.



C-Index/II Home Page

www.triosystems.com © Copyright 1996 - 1999 Trio Systems LLC

C-Index/II User Guide © Copyright 1983-1997 Trio Systems LLC

User Guide Revision Date: 5/2/96

All rights reserved. No part of this publication may be reproduced, stored in a retrieval system, or transmitted, in any form or by any means, electronic, mechanical, photocopying, recording, or otherwise, without the prior written permission of Trio Systems LLC.

Trio Systems is a registered trademark of Trio Systems LLC. C-Index, C-Index/II, SpeedRead, ReadShare and PowerFail Protection are exclusive trademarks of Trio Systems LLC.