PageRenderTime 24ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/src/util/vireventglib.c

https://gitlab.com/libvirt/libvirt
C | 499 lines | 365 code | 106 blank | 28 comment | 50 complexity | e376a2779b01595424891eb17aedb94a MD5 | raw file
Possible License(s): GPL-2.0
  1. /*
  2. * vireventglib.c: GMainContext based event loop
  3. *
  4. * Copyright (C) 2008 Daniel P. Berrange
  5. * Copyright (C) 2010-2019 Red Hat, Inc.
  6. *
  7. * This library is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU Lesser General Public
  9. * License as published by the Free Software Foundation; either
  10. * version 2.1 of the License, or (at your option) any later version.
  11. *
  12. * This library is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. * Lesser General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Lesser General Public
  18. * License along with this library. If not, see
  19. * <http://www.gnu.org/licenses/>.
  20. */
  21. #include <config.h>
  22. #include <stdio.h>
  23. #include <string.h>
  24. #include <stdlib.h>
  25. #include "vireventglib.h"
  26. #include "vireventglibwatch.h"
  27. #include "virerror.h"
  28. #include "virlog.h"
  29. #include "virprobe.h"
  30. #ifdef G_OS_WIN32
  31. # include <io.h>
  32. #endif
  33. #define VIR_FROM_THIS VIR_FROM_EVENT
  34. VIR_LOG_INIT("util.eventglib");
  35. struct virEventGLibHandle
  36. {
  37. int watch;
  38. int fd;
  39. int events;
  40. int removed;
  41. guint source;
  42. virEventHandleCallback cb;
  43. void *opaque;
  44. virFreeCallback ff;
  45. };
  46. struct virEventGLibTimeout
  47. {
  48. int timer;
  49. int interval;
  50. int removed;
  51. guint source;
  52. virEventTimeoutCallback cb;
  53. void *opaque;
  54. virFreeCallback ff;
  55. };
  56. static GMutex *eventlock;
  57. static int nextwatch = 1;
  58. static GPtrArray *handles;
  59. static int nexttimer = 1;
  60. static GPtrArray *timeouts;
  61. static GIOCondition
  62. virEventGLibEventsToCondition(int events)
  63. {
  64. GIOCondition cond = 0;
  65. if (events & VIR_EVENT_HANDLE_READABLE)
  66. cond |= G_IO_IN;
  67. if (events & VIR_EVENT_HANDLE_WRITABLE)
  68. cond |= G_IO_OUT;
  69. if (events & VIR_EVENT_HANDLE_ERROR)
  70. cond |= G_IO_ERR;
  71. if (events & VIR_EVENT_HANDLE_HANGUP)
  72. cond |= G_IO_HUP;
  73. return cond;
  74. }
  75. static int
  76. virEventGLibConditionToEvents(GIOCondition cond)
  77. {
  78. int events = 0;
  79. if (cond & G_IO_IN)
  80. events |= VIR_EVENT_HANDLE_READABLE;
  81. if (cond & G_IO_OUT)
  82. events |= VIR_EVENT_HANDLE_WRITABLE;
  83. if (cond & G_IO_ERR)
  84. events |= VIR_EVENT_HANDLE_ERROR;
  85. if (cond & G_IO_NVAL) /* Treat NVAL as error, since libvirt doesn't distinguish */
  86. events |= VIR_EVENT_HANDLE_ERROR;
  87. if (cond & G_IO_HUP)
  88. events |= VIR_EVENT_HANDLE_HANGUP;
  89. return events;
  90. }
  91. static gboolean
  92. virEventGLibHandleDispatch(int fd G_GNUC_UNUSED,
  93. GIOCondition condition,
  94. gpointer opaque)
  95. {
  96. struct virEventGLibHandle *data = opaque;
  97. int events = virEventGLibConditionToEvents(condition);
  98. VIR_DEBUG("Dispatch handler data=%p watch=%d fd=%d events=%d opaque=%p",
  99. data, data->watch, data->fd, events, data->opaque);
  100. PROBE(EVENT_GLIB_DISPATCH_HANDLE,
  101. "watch=%d events=%d cb=%p opaque=%p",
  102. data->watch, events, data->cb, data->opaque);
  103. (data->cb)(data->watch, data->fd, events, data->opaque);
  104. return TRUE;
  105. }
  106. static int
  107. virEventGLibHandleAdd(int fd,
  108. int events,
  109. virEventHandleCallback cb,
  110. void *opaque,
  111. virFreeCallback ff)
  112. {
  113. struct virEventGLibHandle *data;
  114. GIOCondition cond = virEventGLibEventsToCondition(events);
  115. int ret;
  116. g_mutex_lock(eventlock);
  117. data = g_new0(struct virEventGLibHandle, 1);
  118. data->watch = nextwatch++;
  119. data->fd = fd;
  120. data->events = events;
  121. data->cb = cb;
  122. data->opaque = opaque;
  123. data->ff = ff;
  124. VIR_DEBUG("Add handle data=%p watch=%d fd=%d events=%d opaque=%p",
  125. data, data->watch, data->fd, events, data->opaque);
  126. if (events != 0) {
  127. data->source = virEventGLibAddSocketWatch(
  128. fd, cond, NULL, virEventGLibHandleDispatch, data, NULL);
  129. }
  130. g_ptr_array_add(handles, data);
  131. ret = data->watch;
  132. PROBE(EVENT_GLIB_ADD_HANDLE,
  133. "watch=%d fd=%d events=%d cb=%p opaque=%p ff=%p",
  134. ret, fd, events, cb, opaque, ff);
  135. g_mutex_unlock(eventlock);
  136. return ret;
  137. }
  138. static struct virEventGLibHandle *
  139. virEventGLibHandleFind(int watch)
  140. {
  141. guint i;
  142. for (i = 0; i < handles->len; i++) {
  143. struct virEventGLibHandle *h = g_ptr_array_index(handles, i);
  144. if (h == NULL) {
  145. g_warn_if_reached();
  146. continue;
  147. }
  148. if ((h->watch == watch) && !h->removed)
  149. return h;
  150. }
  151. return NULL;
  152. }
  153. static void
  154. virEventGLibHandleUpdate(int watch,
  155. int events)
  156. {
  157. struct virEventGLibHandle *data;
  158. PROBE(EVENT_GLIB_UPDATE_HANDLE,
  159. "watch=%d events=%d",
  160. watch, events);
  161. g_mutex_lock(eventlock);
  162. data = virEventGLibHandleFind(watch);
  163. if (!data) {
  164. VIR_DEBUG("Update for missing handle watch=%d", watch);
  165. goto cleanup;
  166. }
  167. VIR_DEBUG("Update handle data=%p watch=%d fd=%d events=%d",
  168. data, watch, data->fd, events);
  169. if (events != 0) {
  170. GIOCondition cond = virEventGLibEventsToCondition(events);
  171. if (events == data->events)
  172. goto cleanup;
  173. if (data->source != 0) {
  174. VIR_DEBUG("Removed old handle watch=%d", data->source);
  175. g_source_remove(data->source);
  176. }
  177. data->source = virEventGLibAddSocketWatch(
  178. data->fd, cond, NULL, virEventGLibHandleDispatch, data, NULL);
  179. data->events = events;
  180. VIR_DEBUG("Added new handle watch=%d", data->source);
  181. } else {
  182. if (data->source == 0)
  183. goto cleanup;
  184. VIR_DEBUG("Removed old handle watch=%d", data->source);
  185. g_source_remove(data->source);
  186. data->source = 0;
  187. data->events = 0;
  188. }
  189. cleanup:
  190. g_mutex_unlock(eventlock);
  191. }
  192. static gboolean
  193. virEventGLibHandleRemoveIdle(gpointer data)
  194. {
  195. struct virEventGLibHandle *h = data;
  196. PROBE(EVENT_GLIB_REMOVE_HANDLE_IDLE,
  197. "watch=%d ff=%p opaque=%p",
  198. h->watch, h->ff, h->opaque);
  199. if (h->ff)
  200. (h->ff)(h->opaque);
  201. g_mutex_lock(eventlock);
  202. g_ptr_array_remove_fast(handles, h);
  203. g_mutex_unlock(eventlock);
  204. return FALSE;
  205. }
  206. static int
  207. virEventGLibHandleRemove(int watch)
  208. {
  209. struct virEventGLibHandle *data;
  210. int ret = -1;
  211. PROBE(EVENT_GLIB_REMOVE_HANDLE,
  212. "watch=%d",
  213. watch);
  214. g_mutex_lock(eventlock);
  215. data = virEventGLibHandleFind(watch);
  216. if (!data) {
  217. VIR_DEBUG("Remove of missing handle watch=%d", watch);
  218. goto cleanup;
  219. }
  220. VIR_DEBUG("Remove handle data=%p watch=%d fd=%d",
  221. data, watch, data->fd);
  222. if (data->source != 0) {
  223. g_source_remove(data->source);
  224. data->source = 0;
  225. data->events = 0;
  226. }
  227. /* since the actual watch deletion is done asynchronously, a handleUpdate call may
  228. * reschedule the watch before it's fully deleted, that's why we need to mark it as
  229. * 'removed' to prevent reuse
  230. */
  231. data->removed = TRUE;
  232. g_idle_add(virEventGLibHandleRemoveIdle, data);
  233. ret = 0;
  234. cleanup:
  235. g_mutex_unlock(eventlock);
  236. return ret;
  237. }
  238. static gboolean
  239. virEventGLibTimeoutDispatch(void *opaque)
  240. {
  241. struct virEventGLibTimeout *data = opaque;
  242. VIR_DEBUG("Dispatch timeout data=%p cb=%p timer=%d opaque=%p",
  243. data, data->cb, data->timer, data->opaque);
  244. PROBE(EVENT_GLIB_DISPATCH_TIMEOUT,
  245. "timer=%d cb=%p opaque=%p",
  246. data->timer, data->cb, data->opaque);
  247. (data->cb)(data->timer, data->opaque);
  248. return TRUE;
  249. }
  250. static int
  251. virEventGLibTimeoutAdd(int interval,
  252. virEventTimeoutCallback cb,
  253. void *opaque,
  254. virFreeCallback ff)
  255. {
  256. struct virEventGLibTimeout *data;
  257. int ret;
  258. g_mutex_lock(eventlock);
  259. data = g_new0(struct virEventGLibTimeout, 1);
  260. data->timer = nexttimer++;
  261. data->interval = interval;
  262. data->cb = cb;
  263. data->opaque = opaque;
  264. data->ff = ff;
  265. if (interval >= 0)
  266. data->source = g_timeout_add(interval,
  267. virEventGLibTimeoutDispatch,
  268. data);
  269. g_ptr_array_add(timeouts, data);
  270. VIR_DEBUG("Add timeout data=%p interval=%d ms cb=%p opaque=%p timer=%d",
  271. data, interval, cb, opaque, data->timer);
  272. ret = data->timer;
  273. PROBE(EVENT_GLIB_ADD_TIMEOUT,
  274. "timer=%d interval=%d cb=%p opaque=%p ff=%p",
  275. ret, interval, cb, opaque, ff);
  276. g_mutex_unlock(eventlock);
  277. return ret;
  278. }
  279. static struct virEventGLibTimeout *
  280. virEventGLibTimeoutFind(int timer)
  281. {
  282. guint i;
  283. g_return_val_if_fail(timeouts != NULL, NULL);
  284. for (i = 0; i < timeouts->len; i++) {
  285. struct virEventGLibTimeout *t = g_ptr_array_index(timeouts, i);
  286. if (t == NULL) {
  287. g_warn_if_reached();
  288. continue;
  289. }
  290. if ((t->timer == timer) && !t->removed)
  291. return t;
  292. }
  293. return NULL;
  294. }
  295. static void
  296. virEventGLibTimeoutUpdate(int timer,
  297. int interval)
  298. {
  299. struct virEventGLibTimeout *data;
  300. PROBE(EVENT_GLIB_UPDATE_TIMEOUT,
  301. "timer=%d interval=%d",
  302. timer, interval);
  303. g_mutex_lock(eventlock);
  304. data = virEventGLibTimeoutFind(timer);
  305. if (!data) {
  306. VIR_DEBUG("Update of missing timeout timer=%d", timer);
  307. goto cleanup;
  308. }
  309. VIR_DEBUG("Update timeout data=%p timer=%d interval=%d ms", data, timer, interval);
  310. if (interval >= 0) {
  311. if (data->source != 0)
  312. g_source_remove(data->source);
  313. data->interval = interval;
  314. data->source = g_timeout_add(data->interval,
  315. virEventGLibTimeoutDispatch,
  316. data);
  317. } else {
  318. if (data->source == 0)
  319. goto cleanup;
  320. g_source_remove(data->source);
  321. data->source = 0;
  322. }
  323. cleanup:
  324. g_mutex_unlock(eventlock);
  325. }
  326. static gboolean
  327. virEventGLibTimeoutRemoveIdle(gpointer data)
  328. {
  329. struct virEventGLibTimeout *t = data;
  330. PROBE(EVENT_GLIB_REMOVE_TIMEOUT_IDLE,
  331. "timer=%d ff=%p opaque=%p",
  332. t->timer, t->ff, t->opaque);
  333. if (t->ff)
  334. (t->ff)(t->opaque);
  335. g_mutex_lock(eventlock);
  336. g_ptr_array_remove_fast(timeouts, t);
  337. g_mutex_unlock(eventlock);
  338. return FALSE;
  339. }
  340. static int
  341. virEventGLibTimeoutRemove(int timer)
  342. {
  343. struct virEventGLibTimeout *data;
  344. int ret = -1;
  345. PROBE(EVENT_GLIB_REMOVE_TIMEOUT,
  346. "timer=%d",
  347. timer);
  348. g_mutex_lock(eventlock);
  349. data = virEventGLibTimeoutFind(timer);
  350. if (!data) {
  351. VIR_DEBUG("Remove of missing timeout timer=%d", timer);
  352. goto cleanup;
  353. }
  354. VIR_DEBUG("Remove timeout data=%p timer=%d",
  355. data, timer);
  356. if (data->source != 0) {
  357. g_source_remove(data->source);
  358. data->source = 0;
  359. }
  360. /* since the actual timeout deletion is done asynchronously, a timeoutUpdate call may
  361. * reschedule the timeout before it's fully deleted, that's why we need to mark it as
  362. * 'removed' to prevent reuse
  363. */
  364. data->removed = TRUE;
  365. g_idle_add(virEventGLibTimeoutRemoveIdle, data);
  366. ret = 0;
  367. cleanup:
  368. g_mutex_unlock(eventlock);
  369. return ret;
  370. }
  371. static gpointer virEventGLibRegisterOnce(gpointer data G_GNUC_UNUSED)
  372. {
  373. eventlock = g_new0(GMutex, 1);
  374. timeouts = g_ptr_array_new_with_free_func(g_free);
  375. handles = g_ptr_array_new_with_free_func(g_free);
  376. virEventRegisterImpl(virEventGLibHandleAdd,
  377. virEventGLibHandleUpdate,
  378. virEventGLibHandleRemove,
  379. virEventGLibTimeoutAdd,
  380. virEventGLibTimeoutUpdate,
  381. virEventGLibTimeoutRemove);
  382. return NULL;
  383. }
  384. void virEventGLibRegister(void)
  385. {
  386. static GOnce once = G_ONCE_INIT;
  387. g_once(&once, virEventGLibRegisterOnce, NULL);
  388. }
  389. int virEventGLibRunOnce(void)
  390. {
  391. g_main_context_iteration(NULL, TRUE);
  392. return 0;
  393. }