9proc.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840
  1. #include "stdinc.h"
  2. #include "9.h"
  3. #include "dat.h"
  4. #include "fns.h"
  5. enum {
  6. NConInit = 128,
  7. NMsgInit = 20,
  8. NMsgProcInit = 4,
  9. NMsizeInit = 8192+IOHDRSZ,
  10. };
  11. static struct {
  12. VtLock* alock; /* alloc */
  13. Msg* ahead;
  14. VtRendez* arendez;
  15. int maxmsg;
  16. int nmsg;
  17. int nmsgstarve;
  18. VtLock* rlock; /* read */
  19. Msg* rhead;
  20. Msg* rtail;
  21. VtRendez* rrendez;
  22. int maxproc;
  23. int nproc;
  24. int nprocstarve;
  25. u32int msize; /* immutable */
  26. } mbox;
  27. static struct {
  28. VtLock* alock; /* alloc */
  29. Con* ahead;
  30. VtRendez* arendez;
  31. VtLock* clock;
  32. Con* chead;
  33. Con* ctail;
  34. int maxcon;
  35. int ncon;
  36. int nconstarve;
  37. u32int msize;
  38. } cbox;
  39. static void
  40. conFree(Con* con)
  41. {
  42. assert(con->version == nil);
  43. assert(con->mhead == nil);
  44. assert(con->whead == nil);
  45. assert(con->nfid == 0);
  46. assert(con->state == ConMoribund);
  47. if(con->fd >= 0){
  48. close(con->fd);
  49. con->fd = -1;
  50. }
  51. con->state = ConDead;
  52. vtLock(cbox.alock);
  53. if(con->cprev != nil)
  54. con->cprev->cnext = con->cnext;
  55. else
  56. cbox.chead = con->cnext;
  57. if(con->cnext != nil)
  58. con->cnext->cprev = con->cprev;
  59. else
  60. cbox.ctail = con->cprev;
  61. con->cprev = con->cnext = nil;
  62. if(cbox.ncon > cbox.maxcon){
  63. if(con->name != nil)
  64. vtMemFree(con->name);
  65. vtLockFree(con->fidlock);
  66. vtMemFree(con->data);
  67. vtRendezFree(con->wrendez);
  68. vtLockFree(con->wlock);
  69. vtRendezFree(con->mrendez);
  70. vtLockFree(con->mlock);
  71. vtRendezFree(con->rendez);
  72. vtLockFree(con->lock);
  73. vtMemFree(con);
  74. cbox.ncon--;
  75. vtUnlock(cbox.alock);
  76. return;
  77. }
  78. con->anext = cbox.ahead;
  79. cbox.ahead = con;
  80. if(con->anext == nil)
  81. vtWakeup(cbox.arendez);
  82. vtUnlock(cbox.alock);
  83. }
  84. static void
  85. msgFree(Msg* m)
  86. {
  87. assert(m->rwnext == nil);
  88. assert(m->behind == nil && m->before == nil);
  89. vtLock(mbox.alock);
  90. if(mbox.nmsg > mbox.maxmsg){
  91. vtMemFree(m->data);
  92. vtMemFree(m);
  93. mbox.nmsg--;
  94. vtUnlock(mbox.alock);
  95. return;
  96. }
  97. m->anext = mbox.ahead;
  98. mbox.ahead = m;
  99. if(m->anext == nil)
  100. vtWakeup(mbox.arendez);
  101. vtUnlock(mbox.alock);
  102. }
  103. static Msg*
  104. msgAlloc(Con* con)
  105. {
  106. Msg *m;
  107. vtLock(mbox.alock);
  108. while(mbox.ahead == nil){
  109. if(mbox.nmsg >= mbox.maxmsg){
  110. mbox.nmsgstarve++;
  111. vtSleep(mbox.arendez);
  112. continue;
  113. }
  114. m = vtMemAllocZ(sizeof(Msg));
  115. m->data = vtMemAlloc(mbox.msize);
  116. m->msize = mbox.msize;
  117. mbox.nmsg++;
  118. mbox.ahead = m;
  119. break;
  120. }
  121. m = mbox.ahead;
  122. mbox.ahead = m->anext;
  123. m->anext = nil;
  124. vtUnlock(mbox.alock);
  125. m->con = con;
  126. m->state = MsgR;
  127. return m;
  128. }
  129. static void
  130. msgMunlink(Msg* m)
  131. {
  132. Con *con;
  133. con = m->con;
  134. if(m->mprev != nil)
  135. m->mprev->mnext = m->mnext;
  136. else
  137. con->mhead = m->mnext;
  138. if(m->mnext != nil)
  139. m->mnext->mprev = m->mprev;
  140. else
  141. con->mtail = m->mprev;
  142. m->mprev = m->mnext = nil;
  143. }
  144. static void
  145. msgUnlinkAndFree(Msg* m)
  146. {
  147. /*
  148. * Unlink the message from the flush
  149. * and message lists, and free the message.
  150. * Called with con->mlock locked.
  151. */
  152. if(m->behind){
  153. m->behind->before = nil;
  154. m->behind = nil;
  155. }
  156. if(m->before){
  157. m->before->behind = nil;
  158. m->before = nil;
  159. }
  160. msgMunlink(m);
  161. msgFree(m);
  162. }
  163. void
  164. msgFlush(Msg* m)
  165. {
  166. Con *con;
  167. Msg *old;
  168. con = m->con;
  169. if(Dflag)
  170. fprint(2, "msgFlush %F\n", &m->t);
  171. /*
  172. * If this Tflush has been flushed, nothing to do.
  173. * Look for the message to be flushed in the
  174. * queue of all messages still on this connection.
  175. * If it's not found must assume Elvis has already
  176. * left the building and reply normally.
  177. */
  178. vtLock(con->mlock);
  179. if(m->state == MsgF){
  180. vtUnlock(con->mlock);
  181. return;
  182. }
  183. for(old = con->mhead; old != nil; old = old->mnext)
  184. if(old->t.tag == m->t.oldtag)
  185. break;
  186. if(old == nil){
  187. if(Dflag)
  188. fprint(2, "msgFlush: cannot find %d\n", m->t.oldtag);
  189. vtUnlock(con->mlock);
  190. return;
  191. }
  192. if(Dflag)
  193. fprint(2, "\tmsgFlush found %F\n", &old->t);
  194. /*
  195. * Found it.
  196. * There are two cases where the old message can be
  197. * truly flushed and no reply to the original message given.
  198. * The first is when the old message is in MsgR state; no
  199. * processing has been done yet and it is still on the read
  200. * queue. The second is if old is a Tflush, which doesn't
  201. * affect the server state. In both cases, put the old
  202. * message into MsgF state and let MsgProc or MsgWrite
  203. * toss it after pulling it off the appropriate queue.
  204. */
  205. if(old->state == MsgR || old->t.type == Tflush){
  206. old->state = MsgF;
  207. if(Dflag)
  208. fprint(2, "msgFlush: change %d from MsgR to MsgF\n",
  209. m->t.oldtag);
  210. }
  211. /*
  212. * Link this flush message and the old message
  213. * so we can coalesce multiple flushes (if there are
  214. * multiple Tflush messages for a particular pending
  215. * request, it is only necessary to respond to the last
  216. * one, so any previous can be removed) and to be
  217. * sure flushes wait for their corresponding old
  218. * message to go out first.
  219. */
  220. if(old->behind){
  221. if(Dflag)
  222. fprint(2, "msgFlush: remove %d from %d list\n",
  223. old->behind->t.tag, old->t.tag);
  224. msgUnlinkAndFree(old->behind);
  225. }
  226. old->behind = m;
  227. m->before = old;
  228. if(Dflag)
  229. fprint(2, "msgFlush: add %d to %d queue\n",
  230. m->t.tag, old->t.tag);
  231. vtUnlock(con->mlock);
  232. }
  233. static void
  234. msgProc(void*)
  235. {
  236. Msg *m;
  237. char *e;
  238. Con *con;
  239. vtThreadSetName("msgProc");
  240. for(;;){
  241. /*
  242. * If surplus to requirements, exit.
  243. * If not, wait for and pull a message off
  244. * the read queue.
  245. */
  246. vtLock(mbox.rlock);
  247. if(mbox.nproc > mbox.maxproc){
  248. mbox.nproc--;
  249. vtUnlock(mbox.rlock);
  250. break;
  251. }
  252. while(mbox.rhead == nil)
  253. vtSleep(mbox.rrendez);
  254. m = mbox.rhead;
  255. mbox.rhead = m->rwnext;
  256. m->rwnext = nil;
  257. vtUnlock(mbox.rlock);
  258. con = m->con;
  259. e = nil;
  260. /*
  261. * If the message has been flushed before
  262. * any 9P processing has started, mark it so
  263. * none will be attempted.
  264. */
  265. vtLock(con->mlock);
  266. if(m->state == MsgF)
  267. e = "flushed";
  268. else
  269. m->state = Msg9;
  270. vtUnlock(con->mlock);
  271. if(e == nil){
  272. /*
  273. * explain this
  274. */
  275. vtLock(con->lock);
  276. if(m->t.type == Tversion){
  277. con->version = m;
  278. con->state = ConDown;
  279. while(con->mhead != m)
  280. vtSleep(con->rendez);
  281. assert(con->state == ConDown);
  282. if(con->version == m){
  283. con->version = nil;
  284. con->state = ConInit;
  285. }
  286. else
  287. e = "Tversion aborted";
  288. }
  289. else if(con->state != ConUp)
  290. e = "connection not ready";
  291. vtUnlock(con->lock);
  292. }
  293. /*
  294. * Dispatch if not error already.
  295. */
  296. m->r.tag = m->t.tag;
  297. if(e == nil && !(*rFcall[m->t.type])(m))
  298. e = vtGetError();
  299. if(e != nil){
  300. m->r.type = Rerror;
  301. m->r.ename = e;
  302. }
  303. else
  304. m->r.type = m->t.type+1;
  305. /*
  306. * Put the message (with reply) on the
  307. * write queue and wakeup the write process.
  308. */
  309. vtLock(con->wlock);
  310. if(con->whead == nil)
  311. con->whead = m;
  312. else
  313. con->wtail->rwnext = m;
  314. con->wtail = m;
  315. vtWakeup(con->wrendez);
  316. vtUnlock(con->wlock);
  317. }
  318. }
  319. static void
  320. msgRead(void* v)
  321. {
  322. Msg *m;
  323. Con *con;
  324. int eof, fd, n;
  325. vtThreadSetName("msgRead");
  326. con = v;
  327. fd = con->fd;
  328. eof = 0;
  329. while(!eof){
  330. m = msgAlloc(con);
  331. while((n = read9pmsg(fd, m->data, con->msize)) == 0)
  332. ;
  333. if(n < 0){
  334. m->t.type = Tversion;
  335. m->t.fid = NOFID;
  336. m->t.tag = NOTAG;
  337. m->t.msize = con->msize;
  338. m->t.version = "9PEoF";
  339. eof = 1;
  340. }
  341. else if(convM2S(m->data, n, &m->t) != n){
  342. if(Dflag)
  343. fprint(2, "msgRead: convM2S error: %s\n",
  344. con->name);
  345. msgFree(m);
  346. continue;
  347. }
  348. if(Dflag)
  349. fprint(2, "msgRead %p: t %F\n", con, &m->t);
  350. vtLock(con->mlock);
  351. if(con->mtail != nil){
  352. m->mprev = con->mtail;
  353. con->mtail->mnext = m;
  354. }
  355. else{
  356. con->mhead = m;
  357. m->mprev = nil;
  358. }
  359. con->mtail = m;
  360. vtUnlock(con->mlock);
  361. vtLock(mbox.rlock);
  362. if(mbox.rhead == nil){
  363. mbox.rhead = m;
  364. if(!vtWakeup(mbox.rrendez)){
  365. if(mbox.nproc < mbox.maxproc){
  366. if(vtThread(msgProc, nil) > 0)
  367. mbox.nproc++;
  368. }
  369. else
  370. mbox.nprocstarve++;
  371. }
  372. /*
  373. * don't need this surely?
  374. vtWakeup(mbox.rrendez);
  375. */
  376. }
  377. else
  378. mbox.rtail->rwnext = m;
  379. mbox.rtail = m;
  380. vtUnlock(mbox.rlock);
  381. }
  382. }
  383. static int
  384. _msgWrite(Msg* m)
  385. {
  386. Con *con;
  387. int eof, msgw, n;
  388. /*
  389. * A message with .before set implies it is waiting
  390. * for the .before message to go out first, so punt
  391. * until the .before message goes out (see below).
  392. */
  393. con = m->con;
  394. if(m->before != nil){
  395. if(Dflag)
  396. fprint(2, "msgWrite %p: delay r %F\n", con, &m->r);
  397. return 0;
  398. }
  399. if(m->state == MsgF)
  400. msgw = 0;
  401. else
  402. msgw = 1;
  403. msgMunlink(m);
  404. vtUnlock(con->mlock);
  405. eof = 0;
  406. if(msgw){
  407. /*
  408. * TODO: optimise this copy away somehow for
  409. * read, stat, etc.
  410. */
  411. assert(n = convS2M(&m->r, con->data, con->msize));
  412. if(write(con->fd, con->data, n) != n)
  413. eof = 1;
  414. }
  415. if(Dflag)
  416. fprint(2, "msgWrite %d: r %F\n", msgw, &m->r);
  417. /*
  418. * Message written or flushed. If it has anything waiting
  419. * for it to have gone out, recurse down the list writing
  420. * them out too.
  421. */
  422. vtLock(con->mlock);
  423. if(m->behind != nil){
  424. m->behind->before = nil;
  425. eof += _msgWrite(m->behind);
  426. m->behind = nil;
  427. }
  428. msgFree(m);
  429. return eof;
  430. }
  431. static void
  432. msgWrite(void* v)
  433. {
  434. Msg *m;
  435. int eof;
  436. Con *con;
  437. vtThreadSetName("msgWrite");
  438. con = v;
  439. if(vtThread(msgRead, con) < 0){
  440. conFree(con);
  441. return;
  442. }
  443. for(;;){
  444. /*
  445. * Wait for and pull a message off the write queue.
  446. */
  447. vtLock(con->wlock);
  448. while(con->whead == nil)
  449. vtSleep(con->wrendez);
  450. m = con->whead;
  451. con->whead = m->rwnext;
  452. m->rwnext = nil;
  453. vtUnlock(con->wlock);
  454. /*
  455. * If the message hasn't been flushed,
  456. * change its state so it will be written
  457. * out.
  458. */
  459. vtLock(con->mlock);
  460. if(m->state != MsgF)
  461. m->state = MsgW;
  462. eof = _msgWrite(m);
  463. vtUnlock(con->mlock);
  464. vtLock(con->lock);
  465. if(eof && con->fd >= 0){
  466. close(con->fd);
  467. con->fd = -1;
  468. }
  469. if(con->state == ConDown)
  470. vtWakeup(con->rendez);
  471. if(con->state == ConMoribund && con->mhead == nil){
  472. vtUnlock(con->lock);
  473. conFree(con);
  474. break;
  475. }
  476. vtUnlock(con->lock);
  477. }
  478. }
  479. Con*
  480. conAlloc(int fd, char* name)
  481. {
  482. Con *con;
  483. vtLock(cbox.alock);
  484. while(cbox.ahead == nil){
  485. if(cbox.ncon >= cbox.maxcon){
  486. cbox.nconstarve++;
  487. vtSleep(cbox.arendez);
  488. continue;
  489. }
  490. con = vtMemAllocZ(sizeof(Con));
  491. con->lock = vtLockAlloc();
  492. con->rendez = vtRendezAlloc(con->lock);
  493. con->data = vtMemAlloc(cbox.msize);
  494. con->msize = cbox.msize;
  495. con->alock = vtLockAlloc();
  496. con->mlock = vtLockAlloc();
  497. con->mrendez = vtRendezAlloc(con->mlock);
  498. con->wlock = vtLockAlloc();
  499. con->wrendez = vtRendezAlloc(con->wlock);
  500. con->fidlock = vtLockAlloc();
  501. cbox.ncon++;
  502. cbox.ahead = con;
  503. break;
  504. }
  505. con = cbox.ahead;
  506. cbox.ahead = con->anext;
  507. con->anext = nil;
  508. if(cbox.ctail != nil){
  509. con->cprev = cbox.ctail;
  510. cbox.ctail->cnext = con;
  511. }
  512. else{
  513. cbox.chead = con;
  514. con->cprev = nil;
  515. }
  516. cbox.ctail = con;
  517. assert(con->mhead == nil);
  518. assert(con->whead == nil);
  519. assert(con->fhead == nil);
  520. assert(con->nfid == 0);
  521. con->state = ConNew;
  522. con->fd = fd;
  523. if(con->name != nil){
  524. vtMemFree(con->name);
  525. con->name = nil;
  526. }
  527. if(name != nil)
  528. con->name = vtStrDup(name);
  529. else
  530. con->name = vtStrDup("unknown");
  531. con->aok = 0;
  532. vtUnlock(cbox.alock);
  533. if(vtThread(msgWrite, con) < 0){
  534. conFree(con);
  535. return nil;
  536. }
  537. return con;
  538. }
  539. static int
  540. cmdMsg(int argc, char* argv[])
  541. {
  542. char *p;
  543. char *usage = "usage: msg [-m nmsg] [-p nproc]";
  544. int maxmsg, nmsg, nmsgstarve, maxproc, nproc, nprocstarve;
  545. maxmsg = maxproc = 0;
  546. ARGBEGIN{
  547. default:
  548. return cliError(usage);
  549. case 'm':
  550. p = ARGF();
  551. if(p == nil)
  552. return cliError(usage);
  553. maxmsg = strtol(argv[0], &p, 0);
  554. if(maxmsg <= 0 || p == argv[0] || *p != '\0')
  555. return cliError(usage);
  556. break;
  557. case 'p':
  558. p = ARGF();
  559. if(p == nil)
  560. return cliError(usage);
  561. maxproc = strtol(argv[0], &p, 0);
  562. if(maxproc <= 0 || p == argv[0] || *p != '\0')
  563. return cliError(usage);
  564. break;
  565. }ARGEND
  566. if(argc)
  567. return cliError(usage);
  568. vtLock(mbox.alock);
  569. if(maxmsg)
  570. mbox.maxmsg = maxmsg;
  571. maxmsg = mbox.maxmsg;
  572. nmsg = mbox.nmsg;
  573. nmsgstarve = mbox.nmsgstarve;
  574. vtUnlock(mbox.alock);
  575. vtLock(mbox.rlock);
  576. if(maxproc)
  577. mbox.maxproc = maxproc;
  578. maxproc = mbox.maxproc;
  579. nproc = mbox.nproc;
  580. nprocstarve = mbox.nprocstarve;
  581. vtUnlock(mbox.rlock);
  582. consPrint("\tmsg -m %d -p %d\n", maxmsg, maxproc);
  583. consPrint("\tnmsg %d nmsgstarve %d nproc %d nprocstarve %d\n",
  584. nmsg, nmsgstarve, nproc, nprocstarve);
  585. return 1;
  586. }
  587. static int
  588. scmp(Fid *a, Fid *b)
  589. {
  590. if(a == 0)
  591. return 1;
  592. if(b == 0)
  593. return -1;
  594. return strcmp(a->uname, b->uname);
  595. }
  596. static Fid*
  597. fidMerge(Fid *a, Fid *b)
  598. {
  599. Fid *s, **l;
  600. l = &s;
  601. while(a || b){
  602. if(scmp(a, b) < 0){
  603. *l = a;
  604. l = &a->sort;
  605. a = a->sort;
  606. }else{
  607. *l = b;
  608. l = &b->sort;
  609. b = b->sort;
  610. }
  611. }
  612. *l = 0;
  613. return s;
  614. }
  615. static Fid*
  616. fidMergeSort(Fid *f)
  617. {
  618. int delay;
  619. Fid *a, *b;
  620. if(f == nil)
  621. return nil;
  622. if(f->sort == nil)
  623. return f;
  624. a = b = f;
  625. delay = 1;
  626. while(a && b){
  627. if(delay) /* easy way to handle 2-element list */
  628. delay = 0;
  629. else
  630. a = a->sort;
  631. if(b = b->sort)
  632. b = b->sort;
  633. }
  634. b = a->sort;
  635. a->sort = nil;
  636. a = fidMergeSort(f);
  637. b = fidMergeSort(b);
  638. return fidMerge(a, b);
  639. }
  640. static int
  641. cmdWho(int argc, char* argv[])
  642. {
  643. char *usage = "usage: who";
  644. int i;
  645. Con *con;
  646. Fid *fid, *last;
  647. ARGBEGIN{
  648. default:
  649. return cliError(usage);
  650. }ARGEND
  651. if(argc > 0)
  652. return cliError(usage);
  653. vtRLock(cbox.clock);
  654. for(con=cbox.chead; con; con=con->cnext){
  655. consPrint("\t%q:", con->name);
  656. vtLock(con->fidlock);
  657. last = nil;
  658. for(i=0; i<NFidHash; i++)
  659. for(fid=con->fidhash[i]; fid; fid=fid->hash)
  660. if(fid->fidno != NOFID && fid->uname){
  661. fid->sort = last;
  662. last = fid;
  663. }
  664. fid = fidMergeSort(last);
  665. last = nil;
  666. for(; fid; last=fid, fid=fid->sort)
  667. if(last==nil || strcmp(fid->uname, last->uname) != 0)
  668. consPrint(" %q", fid->uname);
  669. vtUnlock(con->fidlock);
  670. consPrint("\n");
  671. }
  672. vtRUnlock(cbox.clock);
  673. return 1;
  674. }
  675. void
  676. msgInit(void)
  677. {
  678. mbox.alock = vtLockAlloc();
  679. mbox.arendez = vtRendezAlloc(mbox.alock);
  680. mbox.rlock = vtLockAlloc();
  681. mbox.rrendez = vtRendezAlloc(mbox.rlock);
  682. mbox.maxmsg = NMsgInit;
  683. mbox.maxproc = NMsgProcInit;
  684. mbox.msize = NMsizeInit;
  685. cliAddCmd("msg", cmdMsg);
  686. }
  687. static int
  688. cmdCon(int argc, char* argv[])
  689. {
  690. char *p;
  691. Con *con;
  692. char *usage = "usage: con [-m ncon]";
  693. int maxcon, ncon, nconstarve;
  694. maxcon = 0;
  695. ARGBEGIN{
  696. default:
  697. return cliError(usage);
  698. case 'm':
  699. p = ARGF();
  700. if(p == nil)
  701. return cliError(usage);
  702. maxcon = strtol(argv[0], &p, 0);
  703. if(maxcon <= 0 || p == argv[0] || *p != '\0')
  704. return cliError(usage);
  705. break;
  706. }ARGEND
  707. if(argc)
  708. return cliError(usage);
  709. vtLock(cbox.clock);
  710. if(maxcon)
  711. cbox.maxcon = maxcon;
  712. maxcon = cbox.maxcon;
  713. ncon = cbox.ncon;
  714. nconstarve = cbox.nconstarve;
  715. vtUnlock(cbox.clock);
  716. consPrint("\tcon -m %d\n", maxcon);
  717. consPrint("\tncon %d nconstarve %d\n", ncon, nconstarve);
  718. vtRLock(cbox.clock);
  719. for(con = cbox.chead; con != nil; con = con->cnext){
  720. consPrint("\t%s\n", con->name);
  721. }
  722. vtRUnlock(cbox.clock);
  723. return 1;
  724. }
  725. void
  726. conInit(void)
  727. {
  728. cbox.alock = vtLockAlloc();
  729. cbox.arendez = vtRendezAlloc(cbox.alock);
  730. cbox.clock = vtLockAlloc();
  731. cbox.maxcon = NConInit;
  732. cbox.msize = NMsizeInit;
  733. cliAddCmd("con", cmdCon);
  734. cliAddCmd("who", cmdWho);
  735. }