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