GnuCOBOL  2.0
A free COBOL compiler
vblocking.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2003 Trevor van Bremen
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public License
6  * as published by the Free Software Foundation; either version 2.1,
7  * or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; see the file COPYING.LIB. If
16  * not, write to the Free Software Foundation, 51 Franklin Street, Fifth Floor
17  * Boston, MA 02110-1301 USA
18  */
19 
20 #include "isinternal.h"
21 
22 #define IVBBUFFERLEVEL 4
23 
24 /* Globals */
25 
26 int ivbmaxusedhandle = -1; /* The highest opened file handle */
27 
28 /* Local functions */
29 
30 static int
31 ilockinsert (const int ihandle, const off_t trownumber)
32 {
33  struct DICTINFO *psvbptr;
34  struct VBLOCK *psnewlock = NULL, *pslock;
35  int iindexhandle;
36 
37  psvbptr = psvbfile[ihandle];
38  iindexhandle = psvbptr->iindexhandle;
39  pslock = svbfile[iindexhandle].pslockhead;
40  /* Insertion at head of list */
41  if (pslock == NULL || trownumber < pslock->trownumber) {
42  psnewlock = psvblockallocate (ihandle);
43  if (psnewlock == NULL) {
44  return errno;
45  }
46  psnewlock->ihandle = ihandle;
47  psnewlock->trownumber = trownumber;
48  psnewlock->psnext = pslock;
49  svbfile[iindexhandle].pslockhead = psnewlock;
50  if (svbfile[iindexhandle].pslocktail == NULL) {
51  svbfile[iindexhandle].pslocktail = psnewlock;
52  }
53  return 0;
54  }
55  /* Insertion at tail of list */
56  if (trownumber > svbfile[iindexhandle].pslocktail->trownumber) {
57  psnewlock = psvblockallocate (ihandle);
58  if (psnewlock == NULL) {
59  return errno;
60  }
61  psnewlock->ihandle = ihandle;
62  psnewlock->trownumber = trownumber;
63  svbfile[iindexhandle].pslocktail->psnext = psnewlock;
64  svbfile[iindexhandle].pslocktail = psnewlock;
65  return 0;
66  }
67  /* Position pslock to insertion point (Keep in mind, we insert AFTER) */
68  while (pslock->psnext && trownumber > pslock->psnext->trownumber) {
69  pslock = pslock->psnext;
70  }
71  /* Already locked? */
72  if (pslock->trownumber == trownumber) {
73  if (pslock->ihandle == ihandle) {
74  return 0;
75  } else {
76 #ifdef CISAMLOCKS
77  return 0;
78 #else
79  return ELOCKED;
80 #endif
81  }
82  }
83  psnewlock = psvblockallocate (ihandle);
84  if (psnewlock == NULL) {
85  return errno;
86  }
87  psnewlock->ihandle = ihandle;
88  psnewlock->trownumber = trownumber;
89  /* Insert psnewlock AFTER pslock */
90  psnewlock->psnext = pslock->psnext;
91  pslock->psnext = psnewlock;
92 
93  return 0;
94 }
95 
96 static int
97 ilockdelete (const int ihandle, const off_t trownumber)
98 {
99  struct DICTINFO *psvbptr;
100  struct VBLOCK *pslocktodelete;
101  struct VBLOCK *pslock;
102  int iindexhandle;
103 
104  psvbptr = psvbfile[ihandle];
105  iindexhandle = psvbptr->iindexhandle;
106  pslock = svbfile[iindexhandle].pslockhead;
107  /* Sanity check #1. If it wasn't locked, ignore it! */
108  if (!pslock || pslock->trownumber > trownumber) {
109  return 0;
110  }
111  /* Check if deleting first entry in list */
112  if (pslock->trownumber == trownumber) {
113 #ifndef CISAMLOCKS
114  if (pslock->ihandle != ihandle) {
115  return ELOCKED;
116  }
117 #endif
118  svbfile[iindexhandle].pslockhead = pslock->psnext;
119  if (!svbfile[iindexhandle].pslockhead) {
120  svbfile[iindexhandle].pslocktail = NULL;
121  }
122  vvblockfree (pslock);
123  return 0;
124  }
125  /* Position pslock pointer to previous */
126  while (pslock->psnext && pslock->psnext->trownumber < trownumber) {
127  pslock = pslock->psnext;
128  }
129  /* Sanity check #2 */
130  if (!pslock->psnext || pslock->psnext->trownumber != trownumber) {
131  return 0;
132  }
133  pslocktodelete = pslock->psnext;
134 #ifndef CISAMLOCKS
135  if (pslocktodelete->ihandle != ihandle) {
136  return ELOCKED;
137  }
138 #endif
139  pslock->psnext = pslocktodelete->psnext;
140  if (pslocktodelete == svbfile[iindexhandle].pslocktail) {
141  svbfile[iindexhandle].pslocktail = pslock;
142  }
143  vvblockfree (pslocktodelete);
144 
145  return 0;
146 }
147 
148 /* Global functions */
149 
150 /*
151  * Overview
152  * ========
153  * Ideally, I'd prefer using making use of the high bit for the file open
154  * locks and the row locks. However, this is NOT allowed by Linux.
155  *
156  * After a good deal of deliberation (followed by some snooping with good
157  * old strace), I've decided to make the locking strategy 100% compatible
158  * with Informix(R) CISAM 7.24UC2 as it exists for Linux.
159  * When used in 64-bit mode, it simply extends the values accordingly.
160  *
161  * Index file locks (ALL locks are on the index file)
162  * ==================================================
163  * Non exclusive file open lock
164  * Off:0x7fffffff Len:0x00000001 Typ:RDLOCK
165  * Exclusive file open lock (i.e. ISEXCLLOCK)
166  * Off:0x7fffffff Len:0x00000001 Typ:WRLOCK
167  * Enter a primary function - Non modifying
168  * Off:0x00000000 Len:0x3fffffff Typ:RDLCKW
169  * Enter a primary function - Modifying
170  * Off:0x00000000 Len:0x3fffffff Typ:WRLCKW
171  * Lock a data row (Add the row number to the offset)
172  * Off:0x40000000 Len:0x00000001 Typ:WRLOCK
173  * Lock *ALL* the data rows (i.e. islock is called)
174  * Off:0x40000000 Len:0x3fffffff Typ:WRLOCK
175  */
176 
177 int
178 ivbenter (const int ihandle, const unsigned int imodifying,
179  const unsigned int ispecial)
180 {
181  struct DICTINFO *psvbptr;
182  off_t tlength;
183  size_t iloop;
184  int ilockmode;
185  char cvbnodetmp[VB_NODE_MAX];
186 
187  if (unlikely(ihandle < 0 || ihandle > ivbmaxusedhandle)) {
188  iserrno = EBADARG;
189  return -1;
190  }
191  psvbptr = psvbfile[ihandle];
192  if (unlikely(!psvbptr)) {
193  iserrno = ENOTOPEN;
194  return -1;
195  }
196  for (iloop = 0; iloop < MAXSUBS; iloop++) {
197  if (psvbptr->pskeycurr[iloop]
198  && psvbptr->pskeycurr[iloop]->trownode == -1) {
199  psvbptr->pskeycurr[iloop] = NULL;
200  }
201  }
202  iserrno = 0;
203  if (psvbptr->iisopen && ivbintrans != VBCOMMIT && ivbintrans != VBROLLBACK) {
204  iserrno = ENOTOPEN;
205  return -1;
206  }
207  if ((psvbptr->iopenmode & ISTRANS) && ivbintrans == VBNOTRANS) {
208  iserrno = ENOTRANS;
209  return -1;
210  }
211  psvbptr->iindexchanged = 0;
212  if (psvbptr->iopenmode & ISEXCLLOCK) {
213  psvbptr->iisdictlocked |= 0x01;
214  return 0;
215  }
216  if (psvbptr->iisdictlocked & 0x03) {
217  iserrno = EBADARG;
218  return -1;
219  }
220 /* RXW
221  if (imodifying) {
222  ilockmode = VBWRLCKW;
223  } else {
224  ilockmode = VBRDLCKW;
225  }
226 */
227  if (imodifying) {
228  ilockmode = VBWRLOCK;
229  } else {
230  ilockmode = VBRDLOCK;
231  }
232  tlength = VB_OFFLEN_3F;
233  psvbptr->iindexchanged = 0;
234  if (ivblock (psvbptr->iindexhandle, (off_t)0, tlength, ilockmode)) {
235  iserrno = EFLOCKED;
236  return -1;
237  }
238  psvbptr->iisdictlocked |= 0x01;
239 /* RXW
240  if (unlikely(ispecial)) {
241  if (tvblseek (psvbptr->iindexhandle, (off_t)0, SEEK_SET)) {
242  psvbptr->iisdictlocked = 0;
243  ivbexit (ihandle);
244  iserrno = EBADFILE;
245  return -1;
246  }
247  if (tvbread (psvbptr->iindexhandle, cvbnodetmp, 8) != 8) {
248  psvbptr->iisdictlocked = 0;
249  ivbexit (ihandle);
250  iserrno = EBADFILE;
251  return -1;
252  }
253  psvbptr->inodesize = inl_ldint (&cvbnodetmp[6]) + 1;
254  }
255 */
256  if (ivbblockread (ihandle, 1, (off_t) 1, cvbnodetmp)) {
257  psvbptr->iisdictlocked = 0;
258  ivbexit (ihandle);
259  iserrno = EBADFILE;
260  return -1;
261  }
262  memcpy ((void *)&psvbptr->sdictnode, (void *)cvbnodetmp,
263  sizeof (struct DICTNODE));
264  psvbptr->iisdictlocked |= 0x01;
265 /*
266  * If we're in C-ISAM mode, then there is NO way to determine if a given node
267  * has been updated by some other process. Thus *ANY* change to the index
268  * file needs to invalidate the ENTIRE cache for that table!!!
269  * If we're in VBISAM 64-bit mode, then each node in the index table has a
270  * stamp on it stating the transaction number when it was updated. If this
271  * stamp is BELOW that of the current transaction number, we know that the
272  * associated VBTREE / VBKEY linked lists are still coherent!
273  */
274 #if ISAMMODE == 0
275  if (psvbptr->ttranslast !=
276  inl_ldquad (psvbptr->sdictnode.ctransnumber)) {
277  for (iloop = 0; iloop < MAXSUBS; iloop++) {
278  if (psvbptr->pstree[iloop]) {
279  vvbtreeallfree (ihandle, iloop,
280  psvbptr->pstree[iloop]);
281  }
282  psvbptr->pstree[iloop] = NULL;
283  }
284  }
285 #endif
286  return 0;
287 }
288 
289 int
290 ivbexit (const int ihandle)
291 {
292  struct DICTINFO *psvbptr;
293  struct VBKEY *pskey, *pskeycurr;
294  off_t tlength, ttransnumber;
295  int iresult = 0, iloop, iloop2, isaveerror;
296  char cvbnodetmp[VB_NODE_MAX];
297 
298  isaveerror = iserrno;
299  psvbptr = psvbfile[ihandle];
300  if (!psvbptr || (!psvbptr->iisdictlocked && !(psvbptr->iopenmode & ISEXCLLOCK))) {
301  iserrno = EBADARG;
302  return -1;
303  }
304  ttransnumber = inl_ldquad (psvbptr->sdictnode.ctransnumber);
305  psvbptr->ttranslast = ttransnumber;
306 /* RXW
307  if (psvbptr->iopenmode & ISEXCLLOCK) {
308  return 0;
309  }
310 */
311  if (psvbptr->iisdictlocked & 0x02) {
312  if (!(psvbptr->iisdictlocked & 0x04)) {
313  psvbptr->ttranslast = ttransnumber + 1;
314  inl_stquad (ttransnumber + 1,
315  psvbptr->sdictnode.ctransnumber);
316  }
317  memset (cvbnodetmp, 0, VB_NODE_MAX);
318  memcpy ((void *)cvbnodetmp, (void *)&psvbptr->sdictnode,
319  sizeof (struct DICTNODE));
320  iresult = ivbblockwrite (ihandle, 1, (off_t) 1, cvbnodetmp);
321  if (iresult) {
322  iserrno = EBADFILE;
323  } else {
324  iserrno = 0;
325  }
326  }
327  tlength = VB_OFFLEN_3F;
328  if (ivblock (psvbptr->iindexhandle, (off_t)0, tlength, VBUNLOCK)) {
329  iserrno = errno;
330  return -1;
331  }
332  psvbptr->iisdictlocked = 0;
333  /* Free up any key/tree no longer wanted */
334  for (iloop2 = 0; iloop2 < psvbptr->inkeys; iloop2++) {
335  pskey = psvbptr->pskeycurr[iloop2];
336  /*
337  * This is a REAL fudge factor...
338  * We simply free up all the dynamically allocated memory
339  * associated with non-current nodes above a certain level.
340  * In other words, we wish to keep the CURRENT key and all data
341  * in memory above it for IVBBUFFERLEVEL levels.
342  * Anything *ABOVE* that in the tree is to be purged with
343  * EXTREME prejudice except for the 'current' tree up to the
344  * root.
345  */
346  for (iloop = 0; pskey && iloop < IVBBUFFERLEVEL; iloop++) {
347  if (pskey->psparent->psparent) {
348  pskey = pskey->psparent->psparent->pskeycurr;
349  } else {
350  pskey = NULL;
351  }
352  }
353  if (!pskey) {
354  iserrno = isaveerror;
355  return 0;
356  }
357  while (pskey) {
358  for (pskeycurr = pskey->psparent->pskeyfirst; pskeycurr;
359  pskeycurr = pskeycurr->psnext) {
360  if (pskeycurr != pskey && pskeycurr->pschild) {
361  vvbtreeallfree (ihandle, iloop2, pskeycurr->pschild);
362  pskeycurr->pschild = NULL;
363  }
364  }
365  if (pskey->psparent->psparent) {
366  pskey = pskey->psparent->psparent->pskeycurr;
367  } else {
368  pskey = NULL;
369  }
370  }
371  }
372  if (iresult) {
373  return -1;
374  }
375  iserrno = isaveerror;
376  return 0;
377 }
378 
379 int
380 ivbfileopenlock (const int ihandle, const int imode)
381 {
382  struct DICTINFO *psvbptr;
383  off_t toffset;
384  int ilocktype;
385 /* RXW
386  int iresult;
387 */
388  char cvbnodetmp[VB_NODE_MAX];
389 
390  /* Sanity check - Is ihandle a currently open table? */
391  if (unlikely(ihandle < 0 || ihandle > ivbmaxusedhandle)) {
392  return ENOTOPEN;
393  }
394  psvbptr = psvbfile[ihandle];
395  if (!psvbptr) {
396  return ENOTOPEN;
397  }
398 
399  toffset = VB_OFFLEN_7F;
400 
401  switch (imode) {
402  case 0:
403  ilocktype = VBUNLOCK;
404  break;
405 
406  case 1:
407  ilocktype = VBRDLOCK;
408  break;
409 
410  case 2:
411  ilocktype = VBWRLOCK;
412  break;
413 
414  default:
415  return EBADARG;
416  }
417  if (ivblock (psvbptr->iindexhandle, toffset, (off_t)1, ilocktype)) {
418  return EFLOCKED;
419  }
420  if (imode == 2) {
421  if (ivbblockread (ihandle, 1, (off_t) 1, cvbnodetmp)) {
422  (void)ivblock (psvbptr->iindexhandle, toffset, (off_t)1, VBUNLOCK);
423  return EBADFILE;
424  }
425  memcpy ((void *)&psvbptr->sdictnode, (void *)cvbnodetmp,
426  sizeof (struct DICTNODE));
427  }
428  return 0;
429 }
430 
431 int
432 ivbdatalock (const int ihandle, const int imode, const off_t trownumber)
433 {
434  struct DICTINFO *psvbptr;
435  struct VBLOCK *pslock;
436  off_t tlength = 1, toffset;
437  int iresult = 0;
438 
439  /* Sanity check - Is ihandle a currently open table? */
440  if (unlikely(ihandle < 0 || ihandle > ivbmaxusedhandle)) {
441  return ENOTOPEN;
442  }
443  psvbptr = psvbfile[ihandle];
444  if (!psvbptr) {
445  return ENOTOPEN;
446  }
447  if (psvbptr->iopenmode & ISEXCLLOCK) {
448  return 0;
449  }
450  /*
451  * If this is a FILE (un)lock (row = 0), then we may as well free ALL
452  * locks. Even if CISAMLOCKS is set, we do this!
453  */
454  if (trownumber == 0) {
455  for (pslock = svbfile[psvbptr->iindexhandle].pslockhead; pslock;
456  pslock = pslock->psnext) {
457  ivbdatalock (ihandle, VBUNLOCK, pslock->trownumber);
458  }
459  tlength = VB_OFFLEN_3F;
460  if (imode == VBUNLOCK) {
461  psvbptr->iisdatalocked = 0;
462  } else {
463  psvbptr->iisdatalocked = 1;
464  }
465  } else if (imode == VBUNLOCK) {
466  iresult = ilockdelete (ihandle, trownumber);
467  }
468  if (!iresult) {
469  toffset = VB_OFFLEN_40;
470  iresult = ivblock (psvbptr->iindexhandle, toffset + trownumber,
471  tlength, imode);
472  if (iresult != 0) {
473  return ELOCKED;
474  }
475  }
476  if ((imode != VBUNLOCK) && trownumber) {
477  return ilockinsert (ihandle, trownumber);
478  }
479  return iresult;
480 }
int ivblock(const int ihandle, const off_t toffset, const off_t tlength, const int imode)
Definition: vblowlevel.c:197
#define IVBBUFFERLEVEL
Definition: vblocking.c:22
int inkeys
Definition: isinternal.h:400
struct VBFILE svbfile[128 *3]
Definition: vblowlevel.c:24
struct VBLOCK * psvblockallocate(const int ihandle)
Definition: vbmemio.c:67
struct VBLOCK * pslocktail
Definition: isinternal.h:455
unsigned char iindexchanged
Definition: isinternal.h:434
struct VBTREE * pschild
Definition: isinternal.h:327
struct VBLOCK * psnext
Definition: isinternal.h:318
int ihandle
Definition: isinternal.h:319
#define VBCOMMIT
Definition: isinternal.h:309
int ivbblockread(const int ihandle, const int iisindex, const off_t tblocknumber, char *cbuffer)
Definition: vblowlevel.c:137
#define VBRDLOCK
Definition: isinternal.h:295
int iindexhandle
Definition: isinternal.h:406
struct VBLOCK * pslockhead
Definition: isinternal.h:454
#define VBNOTRANS
Definition: isinternal.h:306
#define VB_NODE_MAX
Definition: isinternal.h:288
int iisopen
Definition: isinternal.h:407
void vvblockfree(struct VBLOCK *pslock)
Definition: vbmemio.c:81
#define unlikely(x)
Definition: common.h:437
struct VBTREE * psparent
Definition: isinternal.h:326
struct VBKEY * pskeycurr
Definition: isinternal.h:342
int ivbexit(const int ihandle)
Definition: vblocking.c:290
int ivbfileopenlock(const int ihandle, const int imode)
Definition: vblocking.c:380
unsigned char iisdictlocked
Definition: isinternal.h:427
off_t ttranslast
Definition: isinternal.h:419
#define VB_OFFLEN_40
Definition: isinternal.h:111
EC ARGUMENT EC EC BOUND EC BOUND EC BOUND EC BOUND TABLE EC DATA EC DATA EC DATA PTR NULL
Definition: exception.def:95
int ivbenter(const int ihandle, const unsigned int imodifying, const unsigned int ispecial)
Definition: vblocking.c:178
struct VBKEY * pskeycurr[32]
Definition: isinternal.h:448
struct DICTNODE sdictnode
Definition: isinternal.h:444
off_t trownode
Definition: isinternal.h:328
#define VB_OFFLEN_7F
Definition: isinternal.h:109
#define VBWRLOCK
Definition: isinternal.h:297
off_t trownumber
Definition: isinternal.h:320
struct DICTINFO * psvbfile[128+1]
Definition: vblowlevel.c:23
#define VB_OFFLEN_3F
Definition: isinternal.h:110
char ctransnumber[8]
Definition: isinternal.h:377
unsigned char iisdatalocked
Definition: isinternal.h:426
int iopenmode
Definition: isinternal.h:412
int ivbblockwrite(const int ihandle, const int iisindex, const off_t tblocknumber, const char *cbuffer)
Definition: vblowlevel.c:167
void vvbtreeallfree(const int ihandle, const int ikeynumber, struct VBTREE *pstree)
Definition: vbmemio.c:105
static void inl_stquad(off_t tvalue, void *pclocation)
Definition: isinternal.h:260
int ivbmaxusedhandle
Definition: vblocking.c:26
#define VBROLLBACK
Definition: isinternal.h:310
struct VBTREE * pstree[32]
Definition: isinternal.h:446
int ivbdatalock(const int ihandle, const int imode, const off_t trownumber)
Definition: vblocking.c:432
static int ilockdelete(const int ihandle, const off_t trownumber)
Definition: vblocking.c:97
static int ilockinsert(const int ihandle, const off_t trownumber)
Definition: vblocking.c:31
#define VBUNLOCK
Definition: isinternal.h:294
struct VBKEY * pskeyfirst
Definition: isinternal.h:340
struct VBTREE * psparent
Definition: isinternal.h:339
static char * cvbnodetmp
Definition: ischeck.c:25
static off_t inl_ldquad(void *pclocation)
Definition: isinternal.h:238
off_t trownumber
Definition: isinternal.h:416
struct VBKEY * psnext
Definition: isinternal.h:324
int iserrno
Definition: vbmemio.c:27
#define MAXSUBS
Definition: isinternal.h:119
int ivbintrans
Definition: istrans.c:23