1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
|
/*
* (c) Copyright 1990-1996 OPEN SOFTWARE FOUNDATION, INC.
* (c) Copyright 1990-1996 HEWLETT-PACKARD COMPANY
* (c) Copyright 1990-1996 DIGITAL EQUIPMENT CORPORATION
* (c) Copyright 1991, 1992 Siemens-Nixdorf Information Systems
* To anyone who acknowledges that this file is provided "AS IS" without
* any express or implied warranty: permission to use, copy, modify, and
* distribute this file for any purpose is hereby granted without fee,
* provided that the above copyright notices and this notice appears in
* all source code copies, and that none of the names listed above be used
* in advertising or publicity pertaining to distribution of the software
* without specific, written prior permission. None of these organizations
* makes any representations about the suitability of this software for
* any purpose.
*/
/*
* Header file for thread synchrounous I/O
*/
#ifndef CMA_THREAD_IO
#define CMA_THREAD_IO
/*
* INCLUDE FILES
*/
#include <cma_config.h>
#include <sys/file.h>
#include <cma.h>
#include <sys/types.h>
#include <sys/time.h>
#include <cma_init.h>
#include <cma_errors.h>
/*
* CONSTANTS
*/
/*
* Define symbols which indicate whether to compile code for obsolete
* "non-blocking mode" flags: FNDELAY and FNBLOCK. If the obsolete
* symbols are defined, and if their replacement symbols are defined
* and are different or if they are undefined, then define a symbol
* that says to compile the code in; otherwise no code will be compiled
* for these obsolete symbols.
*/
#ifdef FNDELAY
# ifdef O_NDELAY
# if O_NDELAY != FNDELAY
# define _CMA_FNDELAY_
# endif
# else
# define _CMA_FNDELAY_
# endif
#endif
#ifdef FNBLOCK
# ifdef O_NONBLOCK
# if O_NONBLOCK != FNBLOCK
# define _CMA_FNBLOCK_
# endif
# else
# define _CMA_FNBLOCK_
# endif
#endif
extern cma_t_boolean cma_is_open(int);
/*
* Maximum number of files (ie, max_fd+1)
*/
#define cma__c_mx_file FD_SETSIZE
/*
* Number of bits per file descriptor bit mask (ie number of bytes * bits/byte)
*/
#define cma__c_nbpm NFDBITS
/*
* TYPE DEFINITIONS
*/
typedef enum CMA__T_IO_TYPE {
cma__c_io_read = 0,
cma__c_io_write = 1,
cma__c_io_except = 2
} cma__t_io_type;
#define cma__c_max_io_type 2
/*
* From our local <sys/types.h>:
*
* typedef long fd_mask;
*
* typedef struct fd_set {
* fd_mask fds_bits[howmany(FD_SETSIZE, NFDBITS)];
* } fd_set;
*
*/
typedef fd_mask cma__t_mask;
typedef fd_set cma__t_file_mask;
/*
* GLOBAL DATA
*/
/*
* Maximum number of files (ie, max_fd+1) as determined by getdtablesize().
*/
extern int cma__g_mx_file;
/*
* Number of submasks (ie "int" sized chunks) per file descriptor mask as
* determined by getdtablesize().
*/
extern int cma__g_nspm;
/*
* MACROS
*/
/*
* Define a constant for the errno value which indicates that the requested
* operation was not performed because it would block the process.
*/
# define cma__is_blocking(s) \
((s == EAGAIN) || (s == EWOULDBLOCK) || (s == EINPROGRESS) || \
(s == EALREADY) || (s == EDEADLK))
/*
* It is necessary to issue an I/O function, before calling cma__io_wait()
* in the following cases:
*
* * This file descriptor has been set non-blocking by CMA
* * This file descriptor has been set non-blocking by the user.
*/
#define cma__issue_io_call(fd) \
( (cma__g_file[fd]->non_blocking) || \
(cma__g_file[fd]->user_fl.user_non_blocking) )
#define cma__set_user_nonblocking(flags) \
/*
* Determine if the file is open
*/
/*
* If the file gets closed while waiting for the mutex cma__g_file[rfd]
* gets set to null. This results in a crash if NDEBUG is set to 0
* since cma__int_lock tries to dereference it to set the mutex ownership
* after it gets the mutex. The following will still set the ownership
* in cma__int_lock so we'll set it back to noone if cma__g_file is null
* when we come back just in case it matters. It shouldn't since its no
* longer in use but.....
* Callers of this should recheck cma__g_file after the reservation to
* make sure continueing makes sense.
*/
#define cma__fd_reserve(rfd) \
{ \
cma__t_int_mutex *__mutex__; \
__mutex__ = cma__g_file[rfd]->mutex; \
cma__int_lock (__mutex__); \
if(cma__g_file[rfd] == (cma__t_file_obj *)cma_c_null_ptr) \
cma__int_unlock(__mutex__); \
}
/*
* Unreserve a file descriptor
*/
#define cma__fd_unreserve(ufd) cma__int_unlock (cma__g_file[ufd]->mutex)
/*
* AND together two select file descriptor masks
*/
#define cma__fdm_and(target,a,b) \
{ \
int __i__ = cma__g_nspm; \
while (__i__--) \
(target)->fds_bits[__i__] = \
(a)->fds_bits[__i__] & (b)->fds_bits[__i__]; \
}
/*
* Clear a bit in a select file descriptor mask
*
* FD_CLR(n, p) := ((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS)))
*/
#define cma__fdm_clr_bit(n,p) FD_CLR (n, p)
/*
* Copy the contents of one file descriptor mask into another. If the
* destination operand is null, do nothing; if the source operand is null,
* simply zero the destination.
*/
#define cma__fdm_copy(src,dst,nfds) { \
if (dst) \
if (src) { \
cma__t_mask *__s__ = (cma__t_mask *)(src); \
cma__t_mask *__d__ = (cma__t_mask *)(dst); \
int __i__; \
for (__i__ = 0; __i__ < (nfds); __i__ += cma__c_nbpm) \
*__d__++ = *__s__++; \
} \
else \
cma__fdm_zero (dst); \
}
/*
* To increment count for each bit set in fd - mask
*/
#define cma__fdm_count_bits(map,count) \
{ \
int __i__ = cma__g_nspm; \
while (__i__--) { \
cma__t_mask __tm__; \
__tm__ = (map)->fds_bits[__i__]; \
while(__tm__) { \
(count)++; \
__tm__ &= ~(__tm__ & (-__tm__)); /* Assumes 2's comp */ \
} \
} \
}
/*
* Test if a bit is set in a select file descriptor mask
*
* FD_ISSET(n,p) := ((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS)))
*/
#define cma__fdm_is_set(n,p) FD_ISSET (n, p)
/*
* OR together two select file descriptor masks
*/
#define cma__fdm_or(target,a,b) \
{ \
int __i__ = cma__g_nspm; \
while (__i__--) \
(target)->fds_bits[__i__] = \
(a)->fds_bits[__i__] | (b)->fds_bits[__i__]; \
}
/*
* Set a bit in a select file descriptor mask
*
* FD_SET(n,p) := ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS)))
*/
#define cma__fdm_set_bit(n,p) FD_SET (n, p)
/*
* Clear a select file descriptor mask.
*/
#define cma__fdm_zero(n) \
cma__memset ((char *) n, 0, cma__g_nspm * sizeof(cma__t_mask))
/*
* CMA "thread-synchronous" I/O read/write operations
*/
/*
* Since all CMA "thread-synchronous" I/O (read or write) operations on
* U*ix follow the exact same structure, the wrapper routines have been
* condensed into a macro.
*
* The steps performed are as follows:
* 1. Check that the file descriptor is a legitimate value.
* 2. Check that the entry in the CMA file "database" which corresponds to
* the file descriptor indicates that the "file" was "opened" by CMA.
* 3. Reserve the file, to serialized access to files. This not only
* simplifies things, but also defends against non-reentrancy.
* 4. If the "file" is "set" for non-blocking I/O, check if we
* have actually set the file non-blocking yet, and if not do so.
* Then, issue the I/O operantion.
* Success or failure is returned immediately, after unreserving the
* file. If the error indicates that the operation would have caused
* the process to block, continue to the next step.
* 5. The I/O prolog adds this "file" to the global bit mask, which
* represents all "files" which have threads waiting to perform I/O on
* them, and causes the thread to block on the condition variable for
* this "file". Periodically, a select is done on this global bit
* mask, and the condition variables corresponding to "files" which
* are ready for I/O are signaled, releasing those waiting threads to
* perform their I/O.
* 6. When the thread returns from the I/O prolog, it can (hopefully)
* perform its operation without blocking the process.
* 7. The I/O epilog clears the bit in the global mask and/or signals the
* the next thread waiting for this "file", as appropriate.
* 8. If the I/O failed, continue to loop.
* 9. Finally, the "file" is unreserved, as we're done with it, and the
* result of the operation is returned.
*
*
* Note: currently, we believe that timeslicing which is based on the
* virtual-time timer does not cause system calls to return EINTR.
* Threfore, any EINTR returns are relayed directly to the caller.
* On platforms which do not support a virtual-time timer, the code
* should probably catch EINTR returns and restart the system call.
*/
/*
* This macro is used for both read-type and write-type functions.
*
* Note: the second call to "func" may require being bracketed in a
* cma__interrupt_disable/cma__interrupt_enable pair, but we'll
* wait and see if this is necessary.
*/
#define cma__ts_func(func,fd,arglist,type,post_process) { \
cma_t_integer __res__; \
cma_t_boolean __done__ = cma_c_false; \
if ((fd < 0) || (fd >= cma__g_mx_file)) return (cma__set_errno (EBADF), -1); \
if (!cma__is_open(fd)) return (cma__set_errno (EBADF), -1); \
cma__fd_reserve (fd); \
if (!cma__is_open(fd)) return (cma__set_errno (EBADF), -1); \
if (cma__issue_io_call(fd)) {\
if ((!cma__g_file[fd]->set_non_blocking) && \
(cma__g_file[fd]->non_blocking == cma_c_true)) \
cma__set_nonblocking(fd); \
cma__interrupt_disable (0); \
TRY { \
__res__ = func arglist; \
} \
CATCH_ALL { \
cma__interrupt_enable (0); \
cma__fd_unreserve (fd); \
RERAISE; \
} \
ENDTRY \
cma__interrupt_enable (0); \
if ((__res__ != -1) \
|| (!cma__is_blocking (errno)) \
|| (cma__g_file[fd]->user_fl.user_non_blocking)) \
__done__ = cma_c_true; \
} \
if (__done__) { \
cma__fd_unreserve (fd); \
} \
else { \
TRY { \
cma__io_prolog (type, fd); \
while (!__done__) { \
cma__io_wait (type, fd); \
__res__ = func arglist; \
if ((__res__ != -1) \
|| (!cma__is_blocking (errno)) \
|| (cma__g_file[fd]->user_fl.user_non_blocking)) \
__done__ = cma_c_true; \
} \
} \
FINALLY { \
cma__io_epilog (type, fd); \
cma__fd_unreserve (fd); \
} \
ENDTRY \
} \
if (__res__ != -1) post_process; \
return __res__; \
}
/*
* Since most CMA "thread-synchronous" I/O ("open"-type) operations on
* U*ix follow the exact same structure, the wrapper routines have been
* condensed into a macro.
*
* The steps performed are as follows:
* 1. Issue the open function.
* 2. If the value returned indicates an error, return it to the caller.
* 3. If the file descriptor returned is larger than what we think is the
* maximum value (ie if it is too big for our database) then bugcheck.
* 4. "Open" the "file" in the CMA file database.
* 5. Return the file descriptor value to the caller.
*
* FIX-ME: for the time being, if the I/O operation returns EINTR, we
* simply return it to the caller; eventually, we should catch this
* and "do the right thing" (if we can figure out what that is).
*/
/*
* This macro is used for all "open"-type functions which return a single file
* desciptor by immediate value.
*/
#define cma__ts_open(func,arglist,post_process) { \
int __fd__; \
TRY { \
cma__int_init (); \
cma__int_lock (cma__g_io_data_mutex); \
__fd__ = func arglist; \
cma__int_unlock (cma__g_io_data_mutex); \
if (__fd__ >= 0 && __fd__ < cma__g_mx_file) \
post_process; \
} \
CATCH_ALL \
{ \
cma__set_errno (EBADF); \
__fd__ = -1; \
} \
ENDTRY \
if (__fd__ >= cma__g_mx_file) \
cma__bugcheck ("cma__ts_open: fd is too large"); \
return __fd__; \
}
/*
* This macro is used for all "open"-type functions which return a pair of file
* desciptors by reference parameter.
*/
#define cma__ts_open2(func,fdpair,arglist,post_process) { \
int __res__; \
TRY { \
cma__int_init (); \
cma__int_lock (cma__g_io_data_mutex); \
__res__ = func arglist; \
cma__int_unlock (cma__g_io_data_mutex); \
if (__res__ >= 0 && fdpair[0] < cma__g_mx_file \
&& fdpair[1] < cma__g_mx_file) \
post_process; \
} \
CATCH_ALL \
{ \
cma__set_errno (EBADF); \
__res__ = -1; \
} \
ENDTRY \
if ((fdpair[0] >= cma__g_mx_file) || (fdpair[1] >= cma__g_mx_file)) \
cma__bugcheck ("cma__ts_open2: one of fd's is too large"); \
return __res__; \
}
/*
* INTERNAL INTERFACES
*/
extern void cma__close_general (int);
extern void cma__init_thread_io (void);
extern cma_t_boolean cma__io_available (cma__t_io_type,int,struct timeval *);
extern void cma__io_epilog (cma__t_io_type,int);
extern void cma__io_prolog (cma__t_io_type,int);
extern void cma__io_wait (cma__t_io_type,int);
extern void cma__open_general (int);
extern void cma__reinit_thread_io (int);
extern void cma__set_nonblocking (int);
extern void cma__set_user_nonblock_flags (int,int);
extern cma_t_boolean cma__is_open (int);
#endif
|