Blob


1 #include "threadimpl.h"
2 #include "BSD.c"
4 #include <dlfcn.h>
6 #define MIN(a,b) ((a) < (b) ? (a) : (b))
7 #define MAX(a,b) ((a) > (b) ? (a) : (b))
9 enum {
10 FD_READ = 1,
11 FD_WRITE,
12 FD_RDWR,
13 FDTBL_MAXSIZE = 128
14 };
16 struct fd_entry {
17 QLock lock;
18 int fd;
19 int id;
20 short rc;
21 short wc;
22 short ref;
23 };
25 static struct fd_entry fd_entry1 = { .fd -1 };
26 static struct fd_entry *fd_table = nil;
27 static spinlock_t fd_table_lock = { 0, 0, nil, 0 };
28 static spinlock_t mlock = { 0, 0, nil, 0 };
29 static spinlock_t dl_lock = { 0, 0, nil, 0 };
31 void
32 _thread_malloc_lock(void)
33 {
34 _spinlock(&mlock);
35 }
37 void
38 _thread_malloc_unlock(void)
39 {
40 _spinunlock(&mlock);
41 }
43 void
44 _thread_malloc_init(void)
45 {
46 }
48 /*
49 * Must set errno on failure because the return value
50 * of _thread_fd_entry is propagated back to the caller
51 * of the thread-wrapped libc function.
52 */
53 static struct fd_entry *
54 _thread_fd_lookup(int fd)
55 {
56 struct fd_entry *t;
57 static int cursize;
58 int newsize;
60 if(fd >= FDTBL_MAXSIZE) {
61 errno = EBADF;
62 return nil;
63 }
65 /*
66 * There are currently only a few libc functions using
67 * _thread_fd_*, which are rarely called by P9P programs.
68 * So the contention for these locks is very small and so
69 * far have usually been limited to a single fd. So
70 * rather than malloc the fd_table everytime we just use
71 * a single fd_entry until a lock request for another fd
72 * comes in.
73 */
74 if(fd_table == nil)
75 if(fd_entry1.fd == -1) {
76 fd_entry1.fd = fd;
77 return &fd_entry1;
78 } else if(fd_entry1.fd == fd)
79 return &fd_entry1;
80 else {
81 cursize = MAX(fd_entry1.fd, 16);
82 fd_table = malloc(cursize*sizeof(fd_table[0]));
83 if(fd_table == nil) {
84 errno = ENOMEM;
85 return nil;
86 }
87 memset(fd_table, 0, cursize*sizeof(fd_table[0]));
88 fd_table[fd_entry1.fd] = fd_entry1;
89 }
90 if(fd > cursize) {
91 newsize = MIN(cursize*2, FDTBL_MAXSIZE);
92 t = realloc(fd_table, newsize*sizeof(fd_table[0]));
93 if(t == nil) {
94 errno = ENOMEM;
95 return nil;
96 }
97 fd_table = t;
98 cursize = newsize;
99 memset(fd_table, 0, cursize*sizeof(fd_table[0]));
102 return &fd_table[fd];
105 /*
106 * Mutiple readers just share the lock by incrementing the read count.
107 * Writers must obtain an exclusive lock.
108 */
109 int
110 _thread_fd_lock(int fd, int type, struct timespec *time)
112 struct fd_entry *fde;
113 int id;
115 _spinlock(&fd_table_lock);
116 fde = _thread_fd_lookup(fd);
117 if(fde == nil)
118 return -1;
120 if(type == FD_READ) {
121 if(fde->rc++ >= 1 && fde->wc == 0) {
122 _spinunlock(&fd_table_lock);
123 return 0;
125 } else
126 fde->wc++;
127 _spinunlock(&fd_table_lock);
129 /* handle recursion */
130 id = proc()->osprocid;
131 if(id == fde->id) {
132 fde->ref++;
133 return 0;
136 qlock(&fde->lock);
137 fde->id = id;
138 return 0;
141 void
142 _thread_fd_unlock(int fd, int type)
144 struct fd_entry *fde;
145 int id;
147 fde = _thread_fd_lookup(fd);
148 if(fde == nil) {
149 fprint(2, "_thread_fd_unlock: fd %d not in table!\n", fd);
150 return;
153 if(type == FD_READ && --fde->rc >= 1)
154 return;
155 else
156 fde->wc--;
158 id = proc()->osprocid;
159 if(id == fde->id && fde->ref > 0) {
160 fde->ref--;
161 return;
163 fde->id = 0;
164 qunlock(&fde->lock);
167 void
168 _thread_dl_lock(int t)
170 if(t)
171 _spinunlock(&dl_lock);
172 else
173 _spinlock(&dl_lock);
176 void
177 _pthreadinit(void)
179 __isthreaded = 1;
180 dlctl(nil, DL_SETTHREADLCK, _thread_dl_lock);
181 signal(SIGUSR2, sigusr2handler);