syssem.c 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. /*
  2. * This file is part of the UCB release of Plan 9. It is subject to the license
  3. * terms in the LICENSE file found in the top-level directory of this
  4. * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
  5. * part of the UCB release of Plan 9, including this file, may be copied,
  6. * modified, propagated, or distributed except according to the terms contained
  7. * in the LICENSE file.
  8. */
  9. #include "u.h"
  10. #include "../port/lib.h"
  11. #include "mem.h"
  12. #include "dat.h"
  13. #include "fns.h"
  14. #include "../port/error.h"
  15. static int semtrytimes = 100;
  16. /*
  17. * Support for user-level optimistic semaphores.
  18. *
  19. * A semaphore is an integer n.
  20. * If n > 0, there are tickets
  21. * If n == 0, there are no tickets
  22. * If n < 0, there are |n| processes waiting for tickets
  23. * or preparing to wait for tickets.
  24. *
  25. * CAUTION: Do not use adec/ainc for semaphores, they would
  26. * trap if the integer is negative, but that's ok for semaphores.
  27. */
  28. static void
  29. semwakeup(Sem *s, int didwake, int dolock)
  30. {
  31. Proc *up = externup();
  32. Proc *p;
  33. DBG("semwakeup up %#p sem %#p\n", up, s->np);
  34. if(dolock)
  35. lock(&s->l);
  36. /*
  37. * If there are more processes sleeping than |*s->np| then
  38. * there are ups not yet seen by sleepers, wake up those that
  39. * have tickets.
  40. */
  41. while(s->nq > 0 && s->nq > - *s->np){
  42. p = s->q[0];
  43. s->nq--;
  44. s->q[0] = s->q[s->nq];
  45. if(didwake){
  46. DBG("semwakeup up %#p waking up %#p\n", up, p);
  47. p->waitsem = s;
  48. /*
  49. * p can be up if it's being killed, in which
  50. * case it might be consuming a ticket being
  51. * put while dying. In that case, another
  52. * process might wait because we would get the
  53. * ticket. Up must call semwakeup to be sure
  54. * that other process no longer sleeps.
  55. * But we can't be put in the scheduler queue.
  56. */
  57. if(p != up)
  58. ready(p);
  59. }
  60. }
  61. if(dolock)
  62. unlock(&s->l);
  63. }
  64. static void
  65. semsleep(Sem *s, int dontblock)
  66. {
  67. Proc *up = externup();
  68. DBG("semsleep up %#p sem %#p\n", up, s->np);
  69. if(dontblock){
  70. /*
  71. * User tried to down non-blocking, but someone else
  72. * got the ticket between looking at n and adec(n).
  73. * we have to safely undo our temporary down here.
  74. * Adjust the value of the semaphore to reflect that we
  75. * wanted a ticket for a while but no longer want one.
  76. * Make sure that no other process is waiting because we
  77. * made a temporary down.
  78. */
  79. semainc(s->np);
  80. semwakeup(s, 1, 1);
  81. return;
  82. }
  83. lock(&s->l);
  84. if(*s->np >= 0){
  85. /*
  86. * A ticket came, either it came while calling the kernel,
  87. * or it was a temporary sleep that didn't block.
  88. * Either way, we are done.
  89. */
  90. unlock(&s->l);
  91. goto Done;
  92. }
  93. /*
  94. * Commited to wait, we'll have to wait until
  95. * some other process changes our state.
  96. */
  97. s->q = realloc(s->q, (s->nq+1) * sizeof s->q[0]);
  98. if(s->q == nil)
  99. panic("semsleep: no memory");
  100. s->q[s->nq++] = up;
  101. up->waitsem = nil;
  102. up->state = Semdown;
  103. unlock(&s->l);
  104. DBG("semsleep up %#p blocked\n", up);
  105. sched();
  106. Done:
  107. DBG("semsleep up %#p awaken\n", up);
  108. if(up->waitsem == nil){
  109. /*
  110. * nobody did awake us, we are probably being
  111. * killed; we no longer want a ticket.
  112. */
  113. lock(&s->l);
  114. semainc(s->np); /* we are no longer waiting; killed */
  115. semwakeup(s, 1, 0);
  116. unlock(&s->l);
  117. }
  118. }
  119. void
  120. syssemsleep(Ar0* ar0, ...)
  121. {
  122. Proc *up = externup();
  123. int *np;
  124. int dontblock;
  125. Sem *s;
  126. Segment *sg;
  127. va_list list;
  128. va_start(list, ar0);
  129. /*
  130. * void semsleep(int*);
  131. */
  132. np = va_arg(list, int*);
  133. np = validaddr(np, sizeof *np, 1);
  134. evenaddr(PTR2UINT(np));
  135. dontblock = va_arg(list, int);
  136. if((sg = seg(up, PTR2UINT(np), 0)) == nil)
  137. error(Ebadarg);
  138. s = segmksem(sg, np);
  139. semsleep(s, dontblock);
  140. va_end(list);
  141. }
  142. void
  143. syssemwakeup(Ar0* ar0, ...)
  144. {
  145. Proc *up = externup();
  146. int *np;
  147. Sem *s;
  148. Segment *sg;
  149. va_list list;
  150. va_start(list, ar0);
  151. /*
  152. * void semwakeup(int*);
  153. */
  154. np = va_arg(list, int*);
  155. np = validaddr(np, sizeof *np, 1);
  156. evenaddr(PTR2UINT(np));
  157. if((sg = seg(up, PTR2UINT(np), 0)) == nil)
  158. error(Ebadarg);
  159. s = segmksem(sg, np);
  160. semwakeup(s, 1, 1);
  161. va_end(list);
  162. }
  163. static void
  164. semdequeue(Sem *s)
  165. {
  166. Proc *up = externup();
  167. int i;
  168. assert(s != nil);
  169. lock(&s->l);
  170. for(i = 0; i < s->nq; i++)
  171. if(s->q[i] == up)
  172. break;
  173. if(i == s->nq){
  174. /*
  175. * We didn't perform a down on s, yet we are no longer queued
  176. * on it; it must be because someone gave us its
  177. * ticket in the mean while. We must put it back.
  178. */
  179. semainc(s->np);
  180. semwakeup(s, 0, 0);
  181. }else{
  182. s->nq--;
  183. s->q[i] = s->q[s->nq];
  184. }
  185. unlock(&s->l);
  186. }
  187. /*
  188. * Alternative down of a Sem in ss[].
  189. * The logic is similar to multiple downs, see comments in semsleep().
  190. */
  191. static int
  192. semalt(Sem *ss[], int n)
  193. {
  194. Proc *up = externup();
  195. int i, j, r;
  196. Sem *s;
  197. DBG("semalt up %#p ss[0] %#p\n", up, ss[0]->np);
  198. r = -1;
  199. for(i = 0; i < n; i++){
  200. s = ss[i];
  201. n = semadec(s->np);
  202. if(n >= 0){
  203. r = i;
  204. goto Done;
  205. }
  206. lock(&s->l);
  207. s->q = realloc(s->q, (s->nq+1) * sizeof s->q[0]);
  208. if(s->q == nil)
  209. panic("semalt: not enough memory");
  210. s->q[s->nq++] = up;
  211. unlock(&s->l);
  212. }
  213. DBG("semalt up %#p blocked\n", up);
  214. up->state = Semdown;
  215. sched();
  216. Done:
  217. DBG("semalt up %#p awaken\n", up);
  218. for(j = 0; j < i; j++){
  219. assert(ss[j] != nil);
  220. if(ss[j] != up->waitsem)
  221. semdequeue(ss[j]);
  222. else
  223. r = j;
  224. }
  225. if(r < 0)
  226. panic("semalt");
  227. return r;
  228. }
  229. void
  230. syssemalt(Ar0 *ar0, ...)
  231. {
  232. Proc *up = externup();
  233. int **sl;
  234. int i, *np, ns;
  235. Segment *sg;
  236. Sem *ksl[16];
  237. va_list list;
  238. va_start(list, ar0);
  239. /*
  240. * void semalt(int*[], int);
  241. */
  242. ar0->i = -1;
  243. sl = va_arg(list, int**);
  244. ns = va_arg(list, int);
  245. sl = validaddr(sl, ns * sizeof(int*), 1);
  246. if(ns > nelem(ksl))
  247. panic("syssemalt: bug: too many semaphores in alt");
  248. for(i = 0; i < ns; i++){
  249. np = sl[i];
  250. np = validaddr(np, sizeof(int), 1);
  251. evenaddr(PTR2UINT(np));
  252. if((sg = seg(up, PTR2UINT(np), 0)) == nil)
  253. error(Ebadarg);
  254. ksl[i] = segmksem(sg, np);
  255. }
  256. ar0->i = semalt(ksl, ns);
  257. va_end(list);
  258. }
  259. /*
  260. * These are the entry points from the C library, adapted
  261. * for use within the kernel, so that kprocs may share sems
  262. * with users.
  263. * They must be run in a process context.
  264. * Within the kernel, semaphores are used through their
  265. * kernel Sem structures, and not directly by their int* value.
  266. * Otherwise, we would have to look up each time they are used.
  267. */
  268. void
  269. upsem(Sem *s)
  270. {
  271. int n;
  272. n = semainc(s->np);
  273. if(n <= 0)
  274. semwakeup(s, 1, 1);
  275. }
  276. int
  277. downsem(Sem *s, int dontblock)
  278. {
  279. int n, i;
  280. for(i = 0; *s->np <= 0 && i < semtrytimes; i++)
  281. yield();
  282. if(*s->np <= 0 && dontblock)
  283. return -1;
  284. n = semadec(s->np);
  285. if(n < 0)
  286. semsleep(s, dontblock);
  287. return 0;
  288. }
  289. int
  290. altsems(Sem *ss[], int n)
  291. {
  292. int i, w;
  293. /* busy wait */
  294. for(w = 0; w < semtrytimes; w++){
  295. for(i = 0; i < n; i++)
  296. if(*ss[i]->np > 0)
  297. break;
  298. if(i < n)
  299. break;
  300. }
  301. for(i = 0; i < n; i++)
  302. if(downsem(ss[i], 1) != -1)
  303. return i;
  304. return semalt(ss, n);
  305. }