Initial import from FreeBSD RELENG_4:
[dragonfly.git] / lib / libc_r / uthread / uthread_fd.c
1 /*
2  * Copyright (c) 1995-1998 John Birrell <jb@cimlogic.com.au>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *      This product includes software developed by John Birrell.
16  * 4. Neither the name of the author nor the names of any co-contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  *
32  * $FreeBSD: src/lib/libc_r/uthread/uthread_fd.c,v 1.16.2.7 2002/10/22 14:44:03 fjoe Exp $
33  *
34  */
35 #include <errno.h>
36 #include <fcntl.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <pthread.h>
40 #include "pthread_private.h"
41
42 #define FDQ_INSERT(q,p)                                 \
43 do {                                                    \
44         TAILQ_INSERT_TAIL(q,p,qe);                      \
45         p->flags |= PTHREAD_FLAGS_IN_FDQ;               \
46 } while (0)
47
48 #define FDQ_REMOVE(q,p)                                 \
49 do {                                                    \
50         if ((p->flags & PTHREAD_FLAGS_IN_FDQ) != 0) {   \
51                 TAILQ_REMOVE(q,p,qe);                   \
52                 p->flags &= ~PTHREAD_FLAGS_IN_FDQ;      \
53         }                                               \
54 } while (0)
55
56
57 /* Static variables: */
58 static  spinlock_t      fd_table_lock   = _SPINLOCK_INITIALIZER;
59
60 /* Prototypes: */
61 #ifdef _FDLOCKS_ENABLED
62 static inline pthread_t fd_next_reader(int fd);
63 static inline pthread_t fd_next_writer(int fd);
64 #endif
65
66
67 /*
68  * This function *must* return -1 and set the thread specific errno
69  * as a system call. This is because the error return from this
70  * function is propagated directly back from thread-wrapped system
71  * calls.
72  */
73
74 int
75 _thread_fd_table_init(int fd)
76 {
77         int     ret = 0;
78         struct fd_table_entry *entry;
79         int     saved_errno;
80
81         if (_thread_initial == NULL)
82                 _thread_init();
83
84         /* Check if the file descriptor is out of range: */
85         if (fd < 0 || fd >= _thread_dtablesize) {
86                 /* Return a bad file descriptor error: */
87                 errno = EBADF;
88                 ret = -1;
89         }
90
91         /*
92          * Check if memory has already been allocated for this file
93          * descriptor: 
94          */
95         else if (_thread_fd_table[fd] != NULL) {
96                 /* Memory has already been allocated. */
97
98         /* Allocate memory for the file descriptor table entry: */
99         } else if ((entry = (struct fd_table_entry *)
100             malloc(sizeof(struct fd_table_entry))) == NULL) {
101                 /* Return an insufficient memory error: */
102                 errno = ENOMEM;
103                 ret = -1;
104         } else {
105                 /* Initialise the file locks: */
106                 memset(&entry->lock, 0, sizeof(entry->lock));
107                 entry->r_owner = NULL;
108                 entry->w_owner = NULL;
109                 entry->r_fname = NULL;
110                 entry->w_fname = NULL;
111                 entry->r_lineno = 0;
112                 entry->w_lineno = 0;
113                 entry->r_lockcount = 0;
114                 entry->w_lockcount = 0;
115
116                 /* Initialise the read/write queues: */
117                 TAILQ_INIT(&entry->r_queue);
118                 TAILQ_INIT(&entry->w_queue);
119
120                 /* Get the flags for the file: */
121                 if (((fd >= 3) || (_pthread_stdio_flags[fd] == -1)) &&
122                     (entry->flags = __sys_fcntl(fd, F_GETFL, 0)) == -1) {
123                         ret = -1;
124                 }
125                 else {
126                         /* Check if a stdio descriptor: */
127                         if ((fd < 3) && (_pthread_stdio_flags[fd] != -1))
128                                 /*
129                                  * Use the stdio flags read by
130                                  * _pthread_init() to avoid
131                                  * mistaking the non-blocking
132                                  * flag that, when set on one
133                                  * stdio fd, is set on all stdio
134                                  * fds.
135                                  */
136                                 entry->flags = _pthread_stdio_flags[fd];
137
138                         /*
139                          * Make the file descriptor non-blocking.
140                          * This might fail if the device driver does
141                          * not support non-blocking calls, or if the
142                          * driver is naturally non-blocking.
143                          */
144                         saved_errno = errno;
145                         __sys_fcntl(fd, F_SETFL,
146                             entry->flags | O_NONBLOCK);
147                         errno = saved_errno;
148
149                         /* Lock the file descriptor table: */
150                         _SPINLOCK(&fd_table_lock);
151
152                         /*
153                          * Check if another thread allocated the
154                          * file descriptor entry while this thread
155                          * was doing the same thing. The table wasn't
156                          * kept locked during this operation because
157                          * it has the potential to recurse.
158                          */
159                         if (_thread_fd_table[fd] == NULL) {
160                                 /* This thread wins: */
161                                 _thread_fd_table[fd] = entry;
162                                 entry = NULL;
163                         }
164
165                         /* Unlock the file descriptor table: */
166                         _SPINUNLOCK(&fd_table_lock);
167                 }
168
169                 /*
170                  * Check if another thread initialised the table entry
171                  * before this one could:
172                  */
173                 if (entry != NULL)
174                         /*
175                          * Throw away the table entry that this thread
176                          * prepared. The other thread wins.
177                          */
178                         free(entry);
179         }
180
181         /* Return the completion status: */
182         return (ret);
183 }
184
185 int
186 _thread_fd_getflags(int fd)
187 {
188         if (_thread_fd_table[fd] != NULL)
189                 return (_thread_fd_table[fd]->flags);
190         else
191                 return (0);
192 }
193
194 void
195 _thread_fd_setflags(int fd, int flags)
196 {
197         if (_thread_fd_table[fd] != NULL)
198                 _thread_fd_table[fd]->flags = flags;
199 }
200
201 #ifdef _FDLOCKS_ENABLED
202 void
203 _thread_fd_unlock(int fd, int lock_type)
204 {
205         struct pthread  *curthread = _get_curthread();
206         int     ret;
207
208         /*
209          * Check that the file descriptor table is initialised for this
210          * entry: 
211          */
212         if ((ret = _thread_fd_table_init(fd)) == 0) {
213                 /*
214                  * Defer signals to protect the scheduling queues from
215                  * access by the signal handler:
216                  */
217                 _thread_kern_sig_defer();
218
219                 /*
220                  * Lock the file descriptor table entry to prevent
221                  * other threads for clashing with the current
222                  * thread's accesses:
223                  */
224                 _SPINLOCK(&_thread_fd_table[fd]->lock);
225
226                 /* Check if the running thread owns the read lock: */
227                 if (_thread_fd_table[fd]->r_owner == curthread) {
228                         /* Check the file descriptor and lock types: */
229                         if (lock_type == FD_READ || lock_type == FD_RDWR) {
230                                 /*
231                                  * Decrement the read lock count for the
232                                  * running thread: 
233                                  */
234                                 _thread_fd_table[fd]->r_lockcount--;
235
236                                 /*
237                                  * Check if the running thread still has read
238                                  * locks on this file descriptor: 
239                                  */
240                                 if (_thread_fd_table[fd]->r_lockcount != 0) {
241                                 }
242                                 /*
243                                  * Get the next thread in the queue for a
244                                  * read lock on this file descriptor: 
245                                  */
246                                 else if ((_thread_fd_table[fd]->r_owner = fd_next_reader(fd)) == NULL) {
247                                 } else {
248                                         /* Remove this thread from the queue: */
249                                         FDQ_REMOVE(&_thread_fd_table[fd]->r_queue,
250                                             _thread_fd_table[fd]->r_owner);
251
252                                         /*
253                                          * Set the state of the new owner of
254                                          * the thread to running: 
255                                          */
256                                         PTHREAD_NEW_STATE(_thread_fd_table[fd]->r_owner,PS_RUNNING);
257
258                                         /*
259                                          * Reset the number of read locks.
260                                          * This will be incremented by the
261                                          * new owner of the lock when it sees
262                                          * that it has the lock.                           
263                                          */
264                                         _thread_fd_table[fd]->r_lockcount = 0;
265                                 }
266                         }
267                 }
268                 /* Check if the running thread owns the write lock: */
269                 if (_thread_fd_table[fd]->w_owner == curthread) {
270                         /* Check the file descriptor and lock types: */
271                         if (lock_type == FD_WRITE || lock_type == FD_RDWR) {
272                                 /*
273                                  * Decrement the write lock count for the
274                                  * running thread: 
275                                  */
276                                 _thread_fd_table[fd]->w_lockcount--;
277
278                                 /*
279                                  * Check if the running thread still has
280                                  * write locks on this file descriptor: 
281                                  */
282                                 if (_thread_fd_table[fd]->w_lockcount != 0) {
283                                 }
284                                 /*
285                                  * Get the next thread in the queue for a
286                                  * write lock on this file descriptor: 
287                                  */
288                                 else if ((_thread_fd_table[fd]->w_owner = fd_next_writer(fd)) == NULL) {
289                                 } else {
290                                         /* Remove this thread from the queue: */
291                                         FDQ_REMOVE(&_thread_fd_table[fd]->w_queue,
292                                             _thread_fd_table[fd]->w_owner);
293
294                                         /*
295                                          * Set the state of the new owner of
296                                          * the thread to running: 
297                                          */
298                                         PTHREAD_NEW_STATE(_thread_fd_table[fd]->w_owner,PS_RUNNING);
299
300                                         /*
301                                          * Reset the number of write locks.
302                                          * This will be incremented by the
303                                          * new owner of the lock when it  
304                                          * sees that it has the lock.
305                                          */
306                                         _thread_fd_table[fd]->w_lockcount = 0;
307                                 }
308                         }
309                 }
310
311                 /* Unlock the file descriptor table entry: */
312                 _SPINUNLOCK(&_thread_fd_table[fd]->lock);
313
314                 /*
315                  * Undefer and handle pending signals, yielding if
316                  * necessary:
317                  */
318                 _thread_kern_sig_undefer();
319         }
320 }
321
322 int
323 _thread_fd_lock(int fd, int lock_type, struct timespec * timeout)
324 {
325         struct pthread  *curthread = _get_curthread();
326         int     ret;
327
328         /*
329          * Check that the file descriptor table is initialised for this
330          * entry: 
331          */
332         if ((ret = _thread_fd_table_init(fd)) == 0) {
333                 /* Clear the interrupted flag: */
334                 curthread->interrupted = 0;
335
336                 /*
337                  * Lock the file descriptor table entry to prevent
338                  * other threads for clashing with the current
339                  * thread's accesses:
340                  */
341                 _SPINLOCK(&_thread_fd_table[fd]->lock);
342
343                 /* Check the file descriptor and lock types: */
344                 if (lock_type == FD_READ || lock_type == FD_RDWR) {
345                         /*
346                          * Wait for the file descriptor to be locked
347                          * for read for the current thread: 
348                          */
349                         while ((_thread_fd_table[fd]->r_owner != curthread) &&
350                             (curthread->interrupted == 0)) {
351                                 /*
352                                  * Check if the file descriptor is locked by
353                                  * another thread: 
354                                  */
355                                 if (_thread_fd_table[fd]->r_owner != NULL) {
356                                         /*
357                                          * Another thread has locked the file
358                                          * descriptor for read, so join the
359                                          * queue of threads waiting for a  
360                                          * read lock on this file descriptor: 
361                                          */
362                                         FDQ_INSERT(&_thread_fd_table[fd]->r_queue, curthread);
363
364                                         /*
365                                          * Save the file descriptor details
366                                          * in the thread structure for the
367                                          * running thread: 
368                                          */
369                                         curthread->data.fd.fd = fd;
370
371                                         /* Set the timeout: */
372                                         _thread_kern_set_timeout(timeout);
373
374                                         /*
375                                          * Unlock the file descriptor
376                                          * table entry:
377                                          */
378                                         _SPINUNLOCK(&_thread_fd_table[fd]->lock);
379
380                                         /*
381                                          * Schedule this thread to wait on
382                                          * the read lock. It will only be
383                                          * woken when it becomes the next in
384                                          * the   queue and is granted access
385                                          * to the lock by the       thread
386                                          * that is unlocking the file
387                                          * descriptor.        
388                                          */
389                                         _thread_kern_sched_state(PS_FDLR_WAIT, __FILE__, __LINE__);
390
391                                         /*
392                                          * Lock the file descriptor
393                                          * table entry again:
394                                          */
395                                         _SPINLOCK(&_thread_fd_table[fd]->lock);
396
397                                         if (curthread->interrupted != 0) {
398                                                 FDQ_REMOVE(&_thread_fd_table[fd]->r_queue,
399                                                     curthread);
400                                         }
401                                 } else {
402                                         /*
403                                          * The running thread now owns the
404                                          * read lock on this file descriptor: 
405                                          */
406                                         _thread_fd_table[fd]->r_owner = curthread;
407
408                                         /*
409                                          * Reset the number of read locks for
410                                          * this file descriptor: 
411                                          */
412                                         _thread_fd_table[fd]->r_lockcount = 0;
413                                 }
414                         }
415
416                         if (_thread_fd_table[fd]->r_owner == curthread)
417                                 /* Increment the read lock count: */
418                                 _thread_fd_table[fd]->r_lockcount++;
419                 }
420
421                 /* Check the file descriptor and lock types: */
422                 if (curthread->interrupted == 0 &&
423                     (lock_type == FD_WRITE || lock_type == FD_RDWR)) {
424                         /*
425                          * Wait for the file descriptor to be locked
426                          * for write for the current thread: 
427                          */
428                         while ((_thread_fd_table[fd]->w_owner != curthread) &&
429                             (curthread->interrupted == 0)) {
430                                 /*
431                                  * Check if the file descriptor is locked by
432                                  * another thread: 
433                                  */
434                                 if (_thread_fd_table[fd]->w_owner != NULL) {
435                                         /*
436                                          * Another thread has locked the file
437                                          * descriptor for write, so join the
438                                          * queue of threads waiting for a 
439                                          * write lock on this file
440                                          * descriptor: 
441                                          */
442                                         FDQ_INSERT(&_thread_fd_table[fd]->w_queue, curthread);
443
444                                         /*
445                                          * Save the file descriptor details
446                                          * in the thread structure for the
447                                          * running thread: 
448                                          */
449                                         curthread->data.fd.fd = fd;
450
451                                         /* Set the timeout: */
452                                         _thread_kern_set_timeout(timeout);
453
454                                         /*
455                                          * Unlock the file descriptor
456                                          * table entry:
457                                          */
458                                         _SPINUNLOCK(&_thread_fd_table[fd]->lock);
459
460                                         /*
461                                          * Schedule this thread to wait on
462                                          * the write lock. It will only be
463                                          * woken when it becomes the next in
464                                          * the queue and is granted access to
465                                          * the lock by the thread that is
466                                          * unlocking the file descriptor.        
467                                          */
468                                         _thread_kern_sched_state(PS_FDLW_WAIT, __FILE__, __LINE__);
469
470                                         /*
471                                          * Lock the file descriptor
472                                          * table entry again:
473                                          */
474                                         _SPINLOCK(&_thread_fd_table[fd]->lock);
475
476                                         if (curthread->interrupted != 0) {
477                                                 FDQ_REMOVE(&_thread_fd_table[fd]->w_queue,
478                                                     curthread);
479                                         }
480                                 } else {
481                                         /*
482                                          * The running thread now owns the
483                                          * write lock on this   file
484                                          * descriptor: 
485                                          */
486                                         _thread_fd_table[fd]->w_owner = curthread;
487
488                                         /*
489                                          * Reset the number of write locks
490                                          * for this file descriptor: 
491                                          */
492                                         _thread_fd_table[fd]->w_lockcount = 0;
493                                 }
494                         }
495
496                         if (_thread_fd_table[fd]->w_owner == curthread)
497                                 /* Increment the write lock count: */
498                                 _thread_fd_table[fd]->w_lockcount++;
499                 }
500
501                 /* Unlock the file descriptor table entry: */
502                 _SPINUNLOCK(&_thread_fd_table[fd]->lock);
503
504                 if (curthread->interrupted != 0) {
505                         ret = -1;
506                         errno = EINTR;
507                         if (curthread->continuation != NULL)
508                                 curthread->continuation((void *)curthread);
509                 }
510         }
511
512         /* Return the completion status: */
513         return (ret);
514 }
515
516 void
517 _thread_fd_unlock_debug(int fd, int lock_type, char *fname, int lineno)
518 {
519         struct pthread  *curthread = _get_curthread();
520         int     ret;
521
522         /*
523          * Check that the file descriptor table is initialised for this
524          * entry: 
525          */
526         if ((ret = _thread_fd_table_init(fd)) == 0) {
527                 /*
528                  * Defer signals to protect the scheduling queues from
529                  * access by the signal handler:
530                  */
531                 _thread_kern_sig_defer();
532
533                 /*
534                  * Lock the file descriptor table entry to prevent
535                  * other threads for clashing with the current
536                  * thread's accesses:
537                  */
538                 _spinlock_debug(&_thread_fd_table[fd]->lock, fname, lineno);
539
540                 /* Check if the running thread owns the read lock: */
541                 if (_thread_fd_table[fd]->r_owner == curthread) {
542                         /* Check the file descriptor and lock types: */
543                         if (lock_type == FD_READ || lock_type == FD_RDWR) {
544                                 /*
545                                  * Decrement the read lock count for the
546                                  * running thread: 
547                                  */
548                                 _thread_fd_table[fd]->r_lockcount--;
549
550                                 /*
551                                  * Check if the running thread still has read
552                                  * locks on this file descriptor: 
553                                  */
554                                 if (_thread_fd_table[fd]->r_lockcount != 0) {
555                                 }
556                                 /*
557                                  * Get the next thread in the queue for a
558                                  * read lock on this file descriptor: 
559                                  */
560                                 else if ((_thread_fd_table[fd]->r_owner = fd_next_reader(fd)) == NULL) {
561                                 } else {
562                                         /* Remove this thread from the queue: */
563                                         FDQ_REMOVE(&_thread_fd_table[fd]->r_queue,
564                                             _thread_fd_table[fd]->r_owner);
565
566                                         /*
567                                          * Set the state of the new owner of
568                                          * the thread to  running: 
569                                          */
570                                         PTHREAD_NEW_STATE(_thread_fd_table[fd]->r_owner,PS_RUNNING);
571
572                                         /*
573                                          * Reset the number of read locks.
574                                          * This will be incremented by the
575                                          * new owner of the lock when it sees
576                                          * that it has the lock.                           
577                                          */
578                                         _thread_fd_table[fd]->r_lockcount = 0;
579                                 }
580                         }
581                 }
582                 /* Check if the running thread owns the write lock: */
583                 if (_thread_fd_table[fd]->w_owner == curthread) {
584                         /* Check the file descriptor and lock types: */
585                         if (lock_type == FD_WRITE || lock_type == FD_RDWR) {
586                                 /*
587                                  * Decrement the write lock count for the
588                                  * running thread: 
589                                  */
590                                 _thread_fd_table[fd]->w_lockcount--;
591
592                                 /*
593                                  * Check if the running thread still has
594                                  * write locks on this file descriptor: 
595                                  */
596                                 if (_thread_fd_table[fd]->w_lockcount != 0) {
597                                 }
598                                 /*
599                                  * Get the next thread in the queue for a
600                                  * write lock on this file descriptor: 
601                                  */
602                                 else if ((_thread_fd_table[fd]->w_owner = fd_next_writer(fd)) == NULL) {
603                                 } else {
604                                         /* Remove this thread from the queue: */
605                                         FDQ_REMOVE(&_thread_fd_table[fd]->w_queue,
606                                             _thread_fd_table[fd]->w_owner);
607
608                                         /*
609                                          * Set the state of the new owner of
610                                          * the thread to running: 
611                                          */
612                                         PTHREAD_NEW_STATE(_thread_fd_table[fd]->w_owner,PS_RUNNING);
613
614                                         /*
615                                          * Reset the number of write locks.
616                                          * This will be incremented by the
617                                          * new owner of the lock when it  
618                                          * sees that it has the lock.
619                                          */
620                                         _thread_fd_table[fd]->w_lockcount = 0;
621                                 }
622                         }
623                 }
624
625                 /* Unlock the file descriptor table entry: */
626                 _SPINUNLOCK(&_thread_fd_table[fd]->lock);
627
628                 /*
629                  * Undefer and handle pending signals, yielding if
630                  * necessary.
631                  */
632                 _thread_kern_sig_undefer();
633         }
634 }
635
636 int
637 _thread_fd_lock_debug(int fd, int lock_type, struct timespec * timeout,
638                 char *fname, int lineno)
639 {
640         struct pthread  *curthread = _get_curthread();
641         int     ret;
642
643         /*
644          * Check that the file descriptor table is initialised for this
645          * entry: 
646          */
647         if ((ret = _thread_fd_table_init(fd)) == 0) {
648                 /* Clear the interrupted flag: */
649                 curthread->interrupted = 0;
650
651                 /*
652                  * Lock the file descriptor table entry to prevent
653                  * other threads for clashing with the current
654                  * thread's accesses:
655                  */
656                 _spinlock_debug(&_thread_fd_table[fd]->lock, fname, lineno);
657
658                 /* Check the file descriptor and lock types: */
659                 if (lock_type == FD_READ || lock_type == FD_RDWR) {
660                         /*
661                          * Wait for the file descriptor to be locked
662                          * for read for the current thread: 
663                          */
664                         while ((_thread_fd_table[fd]->r_owner != curthread) &&
665                             (curthread->interrupted == 0)) {
666                                 /*
667                                  * Check if the file descriptor is locked by
668                                  * another thread: 
669                                  */
670                                 if (_thread_fd_table[fd]->r_owner != NULL) {
671                                         /*
672                                          * Another thread has locked the file
673                                          * descriptor for read, so join the
674                                          * queue of threads waiting for a  
675                                          * read lock on this file descriptor: 
676                                          */
677                                         FDQ_INSERT(&_thread_fd_table[fd]->r_queue, curthread);
678
679                                         /*
680                                          * Save the file descriptor details
681                                          * in the thread structure for the
682                                          * running thread: 
683                                          */
684                                         curthread->data.fd.fd = fd;
685                                         curthread->data.fd.branch = lineno;
686                                         curthread->data.fd.fname = fname;
687
688                                         /* Set the timeout: */
689                                         _thread_kern_set_timeout(timeout);
690
691                                         /*
692                                          * Unlock the file descriptor
693                                          * table entry:
694                                          */
695                                         _SPINUNLOCK(&_thread_fd_table[fd]->lock);
696
697                                         /*
698                                          * Schedule this thread to wait on
699                                          * the read lock. It will only be
700                                          * woken when it becomes the next in
701                                          * the   queue and is granted access
702                                          * to the lock by the       thread
703                                          * that is unlocking the file
704                                          * descriptor.        
705                                          */
706                                         _thread_kern_sched_state(PS_FDLR_WAIT, __FILE__, __LINE__);
707
708                                         /*
709                                          * Lock the file descriptor
710                                          * table entry again:
711                                          */
712                                         _SPINLOCK(&_thread_fd_table[fd]->lock);
713
714                                         if (curthread->interrupted != 0) {
715                                                 FDQ_REMOVE(&_thread_fd_table[fd]->r_queue,
716                                                     curthread);
717                                         }
718                                 } else {
719                                         /*
720                                          * The running thread now owns the
721                                          * read lock on this file descriptor: 
722                                          */
723                                         _thread_fd_table[fd]->r_owner = curthread;
724
725                                         /*
726                                          * Reset the number of read locks for
727                                          * this file descriptor: 
728                                          */
729                                         _thread_fd_table[fd]->r_lockcount = 0;
730
731                                         /*
732                                          * Save the source file details for
733                                          * debugging: 
734                                          */
735                                         _thread_fd_table[fd]->r_fname = fname;
736                                         _thread_fd_table[fd]->r_lineno = lineno;
737                                 }
738                         }
739
740                         if (_thread_fd_table[fd]->r_owner == curthread)
741                                 /* Increment the read lock count: */
742                                 _thread_fd_table[fd]->r_lockcount++;
743                 }
744
745                 /* Check the file descriptor and lock types: */
746                 if (curthread->interrupted == 0 &&
747                     (lock_type == FD_WRITE || lock_type == FD_RDWR)) {
748                         /*
749                          * Wait for the file descriptor to be locked
750                          * for write for the current thread: 
751                          */
752                         while ((_thread_fd_table[fd]->w_owner != curthread) &&
753                             (curthread->interrupted == 0)) {
754                                 /*
755                                  * Check if the file descriptor is locked by
756                                  * another thread: 
757                                  */
758                                 if (_thread_fd_table[fd]->w_owner != NULL) {
759                                         /*
760                                          * Another thread has locked the file
761                                          * descriptor for write, so join the
762                                          * queue of threads waiting for a 
763                                          * write lock on this file
764                                          * descriptor: 
765                                          */
766                                         FDQ_INSERT(&_thread_fd_table[fd]->w_queue, curthread);
767
768                                         /*
769                                          * Save the file descriptor details
770                                          * in the thread structure for the
771                                          * running thread: 
772                                          */
773                                         curthread->data.fd.fd = fd;
774                                         curthread->data.fd.branch = lineno;
775                                         curthread->data.fd.fname = fname;
776
777                                         /* Set the timeout: */
778                                         _thread_kern_set_timeout(timeout);
779
780                                         /*
781                                          * Unlock the file descriptor
782                                          * table entry:
783                                          */
784                                         _SPINUNLOCK(&_thread_fd_table[fd]->lock);
785
786                                         /*
787                                          * Schedule this thread to wait on
788                                          * the write lock. It will only be
789                                          * woken when it becomes the next in
790                                          * the queue and is granted access to
791                                          * the lock by the thread that is
792                                          * unlocking the file descriptor.        
793                                          */
794                                         _thread_kern_sched_state(PS_FDLW_WAIT, __FILE__, __LINE__);
795
796                                         /*
797                                          * Lock the file descriptor
798                                          * table entry again:
799                                          */
800                                         _SPINLOCK(&_thread_fd_table[fd]->lock);
801
802                                         if (curthread->interrupted != 0) {
803                                                 FDQ_REMOVE(&_thread_fd_table[fd]->w_queue,
804                                                     curthread);
805                                         }
806                                 } else {
807                                         /*
808                                          * The running thread now owns the
809                                          * write lock on this   file
810                                          * descriptor: 
811                                          */
812                                         _thread_fd_table[fd]->w_owner = curthread;
813
814                                         /*
815                                          * Reset the number of write locks
816                                          * for this file descriptor: 
817                                          */
818                                         _thread_fd_table[fd]->w_lockcount = 0;
819
820                                         /*
821                                          * Save the source file details for
822                                          * debugging: 
823                                          */
824                                         _thread_fd_table[fd]->w_fname = fname;
825                                         _thread_fd_table[fd]->w_lineno = lineno;
826                                 }
827                         }
828
829                         if (_thread_fd_table[fd]->w_owner == curthread)
830                                 /* Increment the write lock count: */
831                                 _thread_fd_table[fd]->w_lockcount++;
832                 }
833
834                 /* Unlock the file descriptor table entry: */
835                 _SPINUNLOCK(&_thread_fd_table[fd]->lock);
836
837                 if (curthread->interrupted != 0) {
838                         ret = -1;
839                         errno = EINTR;
840                         if (curthread->continuation != NULL)
841                                 curthread->continuation((void *)curthread);
842                 }
843         }
844
845         /* Return the completion status: */
846         return (ret);
847 }
848
849 void
850 _thread_fd_unlock_owned(pthread_t pthread)
851 {
852         int fd;
853
854         for (fd = 0; fd < _thread_dtablesize; fd++) {
855                 if ((_thread_fd_table[fd] != NULL) &&
856                     ((_thread_fd_table[fd]->r_owner == pthread) ||
857                     (_thread_fd_table[fd]->w_owner == pthread))) {
858                         /*
859                          * Defer signals to protect the scheduling queues
860                          * from access by the signal handler:
861                          */
862                         _thread_kern_sig_defer();
863
864                         /*
865                          * Lock the file descriptor table entry to prevent
866                          * other threads for clashing with the current
867                          * thread's accesses:
868                          */
869                         _SPINLOCK(&_thread_fd_table[fd]->lock);
870
871                         /* Check if the thread owns the read lock: */
872                         if (_thread_fd_table[fd]->r_owner == pthread) {
873                                 /* Clear the read lock count: */
874                                 _thread_fd_table[fd]->r_lockcount = 0;
875
876                                 /*
877                                  * Get the next thread in the queue for a
878                                  * read lock on this file descriptor: 
879                                  */
880                                 if ((_thread_fd_table[fd]->r_owner = fd_next_reader(fd)) != NULL) {
881                                         /* Remove this thread from the queue: */
882                                         FDQ_REMOVE(&_thread_fd_table[fd]->r_queue,
883                                             _thread_fd_table[fd]->r_owner);
884
885                                         /*
886                                          * Set the state of the new owner of
887                                          * the thread to running: 
888                                          */
889                                         PTHREAD_NEW_STATE(_thread_fd_table[fd]->r_owner,PS_RUNNING);
890                                 }
891                         }
892
893                         /* Check if the thread owns the write lock: */
894                         if (_thread_fd_table[fd]->w_owner == pthread) {
895                                 /* Clear the write lock count: */
896                                 _thread_fd_table[fd]->w_lockcount = 0;
897
898                                 /*
899                                  * Get the next thread in the queue for a
900                                  * write lock on this file descriptor: 
901                                  */
902                                 if ((_thread_fd_table[fd]->w_owner = fd_next_writer(fd)) != NULL) {
903                                         /* Remove this thread from the queue: */
904                                         FDQ_REMOVE(&_thread_fd_table[fd]->w_queue,
905                                             _thread_fd_table[fd]->w_owner);
906
907                                         /*
908                                          * Set the state of the new owner of
909                                          * the thread to running: 
910                                          */
911                                         PTHREAD_NEW_STATE(_thread_fd_table[fd]->w_owner,PS_RUNNING);
912
913                                 }
914                         }
915
916                         /* Unlock the file descriptor table entry: */
917                         _SPINUNLOCK(&_thread_fd_table[fd]->lock);
918
919                         /*
920                          * Undefer and handle pending signals, yielding if
921                          * necessary.
922                          */
923                         _thread_kern_sig_undefer();
924                 }
925         }
926 }
927
928 void
929 _fd_lock_backout(pthread_t pthread)
930 {
931         int     fd;
932
933         /*
934          * Defer signals to protect the scheduling queues
935          * from access by the signal handler:
936          */
937         _thread_kern_sig_defer();
938
939         switch (pthread->state) {
940
941         case PS_FDLR_WAIT:
942                 fd = pthread->data.fd.fd;
943
944                 /*
945                  * Lock the file descriptor table entry to prevent
946                  * other threads for clashing with the current
947                  * thread's accesses:
948                  */
949                 _SPINLOCK(&_thread_fd_table[fd]->lock);
950
951                 /* Remove the thread from the waiting queue: */
952                 FDQ_REMOVE(&_thread_fd_table[fd]->r_queue, pthread);
953                 break;
954
955         case PS_FDLW_WAIT:
956                 fd = pthread->data.fd.fd;
957
958                 /*
959                  * Lock the file descriptor table entry to prevent
960                  * other threads from clashing with the current
961                  * thread's accesses:
962                  */
963                 _SPINLOCK(&_thread_fd_table[fd]->lock);
964
965                 /* Remove the thread from the waiting queue: */
966                 FDQ_REMOVE(&_thread_fd_table[fd]->w_queue, pthread);
967                 break;
968
969         default:
970                 break;
971         }
972
973         /*
974          * Undefer and handle pending signals, yielding if
975          * necessary.
976          */
977         _thread_kern_sig_undefer();
978 }
979
980 static inline pthread_t
981 fd_next_reader(int fd)
982 {
983         pthread_t pthread;
984
985         while (((pthread = TAILQ_FIRST(&_thread_fd_table[fd]->r_queue)) != NULL) &&
986             (pthread->interrupted != 0)) {
987                 /*
988                  * This thread has either been interrupted by a signal or
989                  * it has been canceled.  Remove it from the queue.
990                  */
991                 FDQ_REMOVE(&_thread_fd_table[fd]->r_queue, pthread);
992         }
993
994         return (pthread);
995 }
996
997 static inline pthread_t
998 fd_next_writer(int fd)
999 {
1000         pthread_t pthread;
1001
1002         while (((pthread = TAILQ_FIRST(&_thread_fd_table[fd]->w_queue)) != NULL) &&
1003             (pthread->interrupted != 0)) {
1004                 /*
1005                  * This thread has either been interrupted by a signal or
1006                  * it has been canceled.  Remove it from the queue.
1007                  */
1008                 FDQ_REMOVE(&_thread_fd_table[fd]->w_queue, pthread);
1009         }
1010
1011         return (pthread);
1012 }
1013
1014 #else
1015
1016 void
1017 _thread_fd_unlock(int fd, int lock_type)
1018 {
1019 }
1020
1021 int
1022 _thread_fd_lock(int fd, int lock_type, struct timespec * timeout)
1023 {
1024         /*
1025          * Insure that the file descriptor table is initialized for this
1026          * entry: 
1027          */
1028         return (_thread_fd_table_init(fd));
1029 }
1030
1031 void
1032 _thread_fd_unlock_debug(int fd, int lock_type, char *fname, int lineno)
1033 {
1034 }
1035
1036 int
1037 _thread_fd_lock_debug(int fd, int lock_type, struct timespec * timeout,
1038                 char *fname, int lineno)
1039 {
1040         /*
1041          * Insure that the file descriptor table is initialized for this
1042          * entry: 
1043          */
1044         return (_thread_fd_table_init(fd));
1045 }
1046
1047 void
1048 _thread_fd_unlock_owned(pthread_t pthread)
1049 {
1050 }
1051
1052 void
1053 _fd_lock_backout(pthread_t pthread)
1054 {
1055 }
1056
1057 #endif