cmd-wait-for.c 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. /* $OpenBSD$ */
  2. /*
  3. * Copyright (c) 2013 Nicholas Marriott <nicholas.marriott@gmail.com>
  4. * Copyright (c) 2013 Thiago de Arruda <tpadilha84@gmail.com>
  5. *
  6. * Permission to use, copy, modify, and distribute this software for any
  7. * purpose with or without fee is hereby granted, provided that the above
  8. * copyright notice and this permission notice appear in all copies.
  9. *
  10. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  11. * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  12. * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  13. * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  14. * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
  15. * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
  16. * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  17. */
  18. #include <sys/types.h>
  19. #include <stdlib.h>
  20. #include <string.h>
  21. #include "tmux.h"
  22. #include "tmate.h"
  23. /*
  24. * Block or wake a client on a named wait channel.
  25. */
  26. enum cmd_retval cmd_wait_for_exec(struct cmd *, struct cmd_q *);
  27. const struct cmd_entry cmd_wait_for_entry = {
  28. .name = "wait-for",
  29. .alias = "wait",
  30. .args = { "LSU", 1, 1 },
  31. .usage = "[-L|-S|-U] channel",
  32. .flags = 0,
  33. .exec = cmd_wait_for_exec
  34. };
  35. struct wait_channel {
  36. const char *name;
  37. int locked;
  38. int woken;
  39. TAILQ_HEAD(, cmd_q) waiters;
  40. TAILQ_HEAD(, cmd_q) lockers;
  41. RB_ENTRY(wait_channel) entry;
  42. };
  43. RB_HEAD(wait_channels, wait_channel);
  44. struct wait_channels wait_channels = RB_INITIALIZER(wait_channels);
  45. int wait_channel_cmp(struct wait_channel *, struct wait_channel *);
  46. RB_PROTOTYPE(wait_channels, wait_channel, entry, wait_channel_cmp);
  47. RB_GENERATE(wait_channels, wait_channel, entry, wait_channel_cmp);
  48. int
  49. wait_channel_cmp(struct wait_channel *wc1, struct wait_channel *wc2)
  50. {
  51. return (strcmp(wc1->name, wc2->name));
  52. }
  53. enum cmd_retval cmd_wait_for_signal(struct cmd_q *, const char *,
  54. struct wait_channel *);
  55. enum cmd_retval cmd_wait_for_wait(struct cmd_q *, const char *,
  56. struct wait_channel *);
  57. enum cmd_retval cmd_wait_for_lock(struct cmd_q *, const char *,
  58. struct wait_channel *);
  59. enum cmd_retval cmd_wait_for_unlock(struct cmd_q *, const char *,
  60. struct wait_channel *);
  61. struct wait_channel *cmd_wait_for_add(const char *);
  62. void cmd_wait_for_remove(struct wait_channel *wc);
  63. struct wait_channel *
  64. cmd_wait_for_add(const char *name)
  65. {
  66. struct wait_channel *wc;
  67. wc = xmalloc(sizeof *wc);
  68. wc->name = xstrdup(name);
  69. wc->locked = 0;
  70. wc->woken = 0;
  71. TAILQ_INIT(&wc->waiters);
  72. TAILQ_INIT(&wc->lockers);
  73. RB_INSERT(wait_channels, &wait_channels, wc);
  74. log_debug("add wait channel %s", wc->name);
  75. return (wc);
  76. }
  77. void
  78. cmd_wait_for_remove(struct wait_channel *wc)
  79. {
  80. if (wc->locked)
  81. return;
  82. if (!TAILQ_EMPTY(&wc->waiters) || !wc->woken)
  83. return;
  84. log_debug("remove wait channel %s", wc->name);
  85. RB_REMOVE(wait_channels, &wait_channels, wc);
  86. free((void *)wc->name);
  87. free(wc);
  88. }
  89. enum cmd_retval
  90. cmd_wait_for_exec(struct cmd *self, struct cmd_q *cmdq)
  91. {
  92. struct args *args = self->args;
  93. const char *name = args->argv[0];
  94. struct wait_channel *wc, wc0;
  95. wc0.name = name;
  96. wc = RB_FIND(wait_channels, &wait_channels, &wc0);
  97. if (args_has(args, 'S'))
  98. return (cmd_wait_for_signal(cmdq, name, wc));
  99. if (args_has(args, 'L'))
  100. return (cmd_wait_for_lock(cmdq, name, wc));
  101. if (args_has(args, 'U'))
  102. return (cmd_wait_for_unlock(cmdq, name, wc));
  103. return (cmd_wait_for_wait(cmdq, name, wc));
  104. }
  105. enum cmd_retval
  106. cmd_wait_for_signal(__unused struct cmd_q *cmdq, const char *name,
  107. struct wait_channel *wc)
  108. {
  109. struct cmd_q *wq, *wq1;
  110. if (wc == NULL)
  111. wc = cmd_wait_for_add(name);
  112. if (TAILQ_EMPTY(&wc->waiters) && !wc->woken) {
  113. log_debug("signal wait channel %s, no waiters", wc->name);
  114. wc->woken = 1;
  115. return (CMD_RETURN_NORMAL);
  116. }
  117. log_debug("signal wait channel %s, with waiters", wc->name);
  118. TAILQ_FOREACH_SAFE(wq, &wc->waiters, waitentry, wq1) {
  119. TAILQ_REMOVE(&wc->waiters, wq, waitentry);
  120. if (!cmdq_free(wq))
  121. cmdq_continue(wq);
  122. }
  123. cmd_wait_for_remove(wc);
  124. return (CMD_RETURN_NORMAL);
  125. }
  126. #ifdef TMATE
  127. void signal_waiting_clients(const char *name)
  128. {
  129. struct wait_channel *wc, wc0;
  130. struct cmd_q *wq, *wq1;
  131. wc0.name = name;
  132. wc = RB_FIND(wait_channels, &wait_channels, &wc0);
  133. if (wc == NULL || TAILQ_EMPTY(&wc->waiters)) {
  134. return;
  135. }
  136. TAILQ_FOREACH_SAFE(wq, &wc->waiters, waitentry, wq1) {
  137. TAILQ_REMOVE(&wc->waiters, wq, waitentry);
  138. if (!cmdq_free(wq))
  139. cmdq_continue(wq);
  140. }
  141. if (!wc->locked) {
  142. RB_REMOVE(wait_channels, &wait_channels, wc);
  143. free((void*) wc->name);
  144. free(wc);
  145. }
  146. }
  147. #endif
  148. enum cmd_retval
  149. cmd_wait_for_wait(struct cmd_q *cmdq, const char *name,
  150. struct wait_channel *wc)
  151. {
  152. struct client *c = cmdq->client;
  153. #ifdef TMATE
  154. if (!strcmp(name, "tmate-ready") && tmate_session.tmate_env_ready)
  155. return (CMD_RETURN_NORMAL);
  156. #endif
  157. if (c == NULL || c->session != NULL) {
  158. cmdq_error(cmdq, "not able to wait");
  159. return (CMD_RETURN_ERROR);
  160. }
  161. if (wc == NULL)
  162. wc = cmd_wait_for_add(name);
  163. if (wc->woken) {
  164. log_debug("wait channel %s already woken (%p)", wc->name, c);
  165. cmd_wait_for_remove(wc);
  166. return (CMD_RETURN_NORMAL);
  167. }
  168. log_debug("wait channel %s not woken (%p)", wc->name, c);
  169. TAILQ_INSERT_TAIL(&wc->waiters, cmdq, waitentry);
  170. cmdq->references++;
  171. return (CMD_RETURN_WAIT);
  172. }
  173. enum cmd_retval
  174. cmd_wait_for_lock(struct cmd_q *cmdq, const char *name,
  175. struct wait_channel *wc)
  176. {
  177. if (cmdq->client == NULL || cmdq->client->session != NULL) {
  178. cmdq_error(cmdq, "not able to lock");
  179. return (CMD_RETURN_ERROR);
  180. }
  181. if (wc == NULL)
  182. wc = cmd_wait_for_add(name);
  183. if (wc->locked) {
  184. TAILQ_INSERT_TAIL(&wc->lockers, cmdq, waitentry);
  185. cmdq->references++;
  186. return (CMD_RETURN_WAIT);
  187. }
  188. wc->locked = 1;
  189. return (CMD_RETURN_NORMAL);
  190. }
  191. enum cmd_retval
  192. cmd_wait_for_unlock(struct cmd_q *cmdq, const char *name,
  193. struct wait_channel *wc)
  194. {
  195. struct cmd_q *wq;
  196. if (wc == NULL || !wc->locked) {
  197. cmdq_error(cmdq, "channel %s not locked", name);
  198. return (CMD_RETURN_ERROR);
  199. }
  200. if ((wq = TAILQ_FIRST(&wc->lockers)) != NULL) {
  201. TAILQ_REMOVE(&wc->lockers, wq, waitentry);
  202. if (!cmdq_free(wq))
  203. cmdq_continue(wq);
  204. } else {
  205. wc->locked = 0;
  206. cmd_wait_for_remove(wc);
  207. }
  208. return (CMD_RETURN_NORMAL);
  209. }
  210. void
  211. cmd_wait_for_flush(void)
  212. {
  213. struct wait_channel *wc, *wc1;
  214. struct cmd_q *wq, *wq1;
  215. RB_FOREACH_SAFE(wc, wait_channels, &wait_channels, wc1) {
  216. TAILQ_FOREACH_SAFE(wq, &wc->waiters, waitentry, wq1) {
  217. TAILQ_REMOVE(&wc->waiters, wq, waitentry);
  218. if (!cmdq_free(wq))
  219. cmdq_continue(wq);
  220. }
  221. wc->woken = 1;
  222. TAILQ_FOREACH_SAFE(wq, &wc->lockers, waitentry, wq1) {
  223. TAILQ_REMOVE(&wc->lockers, wq, waitentry);
  224. if (!cmdq_free(wq))
  225. cmdq_continue(wq);
  226. }
  227. wc->locked = 0;
  228. cmd_wait_for_remove(wc);
  229. }
  230. }