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