9proc.c 15 KB

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