emnapi 1.2.0 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/CMakeLists.txt +13 -3
  2. package/README.md +7 -18
  3. package/common.gypi +1 -0
  4. package/dist/library_napi.js +130 -41
  5. package/emnapi.gyp +4 -0
  6. package/include/node/emnapi.h +2 -2
  7. package/include/node/js_native_api.h +30 -25
  8. package/include/node/js_native_api_types.h +13 -9
  9. package/include/node/node_api.h +25 -13
  10. package/include/node/uv/threadpool.h +1 -1
  11. package/include/node/uv/unix.h +4 -0
  12. package/include/node/uv.h +49 -6
  13. package/index.js +1 -0
  14. package/lib/wasm32/libemnapi.a +0 -0
  15. package/lib/wasm32-emscripten/libemnapi-mt.a +0 -0
  16. package/lib/wasm32-emscripten/libemnapi.a +0 -0
  17. package/lib/wasm32-wasi/libemnapi.a +0 -0
  18. package/lib/wasm32-wasi-threads/libemnapi-basic-mt.a +0 -0
  19. package/lib/wasm32-wasi-threads/libemnapi-basic.a +0 -0
  20. package/lib/wasm32-wasi-threads/libemnapi-mt.a +0 -0
  21. package/lib/wasm32-wasi-threads/libemnapi.a +0 -0
  22. package/lib/wasm32-wasip1/libemnapi.a +0 -0
  23. package/lib/wasm32-wasip1-threads/libemnapi-basic-mt.a +0 -0
  24. package/lib/wasm32-wasip1-threads/libemnapi-basic.a +0 -0
  25. package/lib/wasm32-wasip1-threads/libemnapi-mt.a +0 -0
  26. package/lib/wasm32-wasip1-threads/libemnapi.a +0 -0
  27. package/lib/wasm64-emscripten/libemnapi-mt.a +0 -0
  28. package/lib/wasm64-emscripten/libemnapi.a +0 -0
  29. package/package.json +1 -1
  30. package/src/async_cleanup_hook.c +1 -1
  31. package/src/async_work.c +2 -2
  32. package/src/emnapi_internal.h +2 -2
  33. package/src/js_native_api.c +2 -1
  34. package/src/node_api.c +5 -5
  35. package/src/threadsafe_function.c +20 -21
  36. package/src/uv/queue.h +68 -86
  37. package/src/uv/threadpool.c +58 -40
  38. package/src/uv/unix/async.c +67 -64
  39. package/src/uv/unix/core.c +36 -1
  40. package/src/uv/unix/internal.h +54 -0
  41. package/src/uv/unix/loop.c +40 -4
  42. package/src/uv/unix/posix-hrtime.c +40 -0
  43. package/src/uv/uv-common.c +123 -7
  44. package/src/uv/uv-common.h +137 -9
@@ -5,9 +5,8 @@
5
5
  #include <stdatomic.h>
6
6
  #include <pthread.h>
7
7
  #include <errno.h>
8
- #include "uv/queue.h"
9
-
10
8
  #include "uv.h"
9
+ #include "uv/queue.h"
11
10
 
12
11
  EXTERN_C_START
13
12
 
@@ -21,7 +20,7 @@ static const unsigned int kMaxIterationCount = 1000;
21
20
 
22
21
  struct data_queue_node {
23
22
  void* data;
24
- void* q[2];
23
+ struct uv__queue q;
25
24
  };
26
25
 
27
26
  struct napi_threadsafe_function__ {
@@ -30,7 +29,7 @@ struct napi_threadsafe_function__ {
30
29
  pthread_mutex_t mutex;
31
30
  pthread_cond_t* cond;
32
31
  size_t queue_size;
33
- void* queue[2];
32
+ struct uv__queue queue;
34
33
  uv_async_t async;
35
34
  size_t thread_count;
36
35
  bool is_closing;
@@ -92,7 +91,7 @@ _emnapi_tsfn_create(napi_env env,
92
91
  pthread_mutex_init(&ts_fn->mutex, NULL);
93
92
  ts_fn->cond = NULL;
94
93
  ts_fn->queue_size = 0;
95
- QUEUE_INIT(&ts_fn->queue);
94
+ uv__queue_init(&ts_fn->queue);
96
95
  ts_fn->thread_count = initial_thread_count;
97
96
  ts_fn->is_closing = false;
98
97
  ts_fn->dispatch_state = kDispatchIdle;
@@ -125,13 +124,13 @@ static void _emnapi_tsfn_destroy(napi_threadsafe_function func) {
125
124
  func->cond = NULL;
126
125
  }
127
126
 
128
- QUEUE* tmp;
127
+ struct uv__queue* tmp;
129
128
  struct data_queue_node* node;
130
- QUEUE_FOREACH(tmp, &func->queue) {
131
- node = QUEUE_DATA(tmp, struct data_queue_node, q);
129
+ uv__queue_foreach(tmp, &func->queue) {
130
+ node = uv__queue_data(tmp, struct data_queue_node, q);
132
131
  free(node);
133
132
  }
134
- QUEUE_INIT(&func->queue);
133
+ uv__queue_init(&func->queue);
135
134
 
136
135
  if (func->ref != NULL) {
137
136
  EMNAPI_ASSERT_CALL(napi_delete_reference(func->env, func->ref));
@@ -187,14 +186,14 @@ static napi_status _emnapi_tsfn_init(napi_threadsafe_function func) {
187
186
  }
188
187
 
189
188
  static void _emnapi_tsfn_empty_queue_and_delete(napi_threadsafe_function func) {
190
- while (!QUEUE_EMPTY(&func->queue)) {
191
- QUEUE* q = QUEUE_HEAD(&func->queue);
192
- struct data_queue_node* node = QUEUE_DATA(q, struct data_queue_node, q);
189
+ while (!uv__queue_empty(&func->queue)) {
190
+ struct uv__queue* q = uv__queue_head(&func->queue);
191
+ struct data_queue_node* node = uv__queue_data(q, struct data_queue_node, q);
193
192
 
194
193
  func->call_js_cb(NULL, NULL, func->context, node->data);
195
194
 
196
- QUEUE_REMOVE(q);
197
- QUEUE_INIT(q);
195
+ uv__queue_remove(q);
196
+ uv__queue_init(q);
198
197
  func->queue_size--;
199
198
  free(node);
200
199
  }
@@ -295,10 +294,10 @@ static bool _emnapi_tsfn_dispatch_one(napi_threadsafe_function func) {
295
294
  } else {
296
295
  size_t size = func->queue_size;
297
296
  if (size > 0) {
298
- QUEUE* q = QUEUE_HEAD(&func->queue);
299
- struct data_queue_node* node = QUEUE_DATA(q, struct data_queue_node, q);
300
- QUEUE_REMOVE(q);
301
- QUEUE_INIT(q);
297
+ struct uv__queue* q = uv__queue_head(&func->queue);
298
+ struct data_queue_node* node = uv__queue_data(q, struct data_queue_node, q);
299
+ uv__queue_remove(q);
300
+ uv__queue_init(q);
302
301
  func->queue_size--;
303
302
  data = node->data;
304
303
  free(node);
@@ -519,7 +518,7 @@ napi_call_threadsafe_function(napi_threadsafe_function func,
519
518
  return napi_generic_failure;
520
519
  }
521
520
  queue_node->data = data;
522
- QUEUE_INSERT_TAIL(&func->queue, &queue_node->q);
521
+ uv__queue_insert_tail(&func->queue, &queue_node->q);
523
522
  func->queue_size++;
524
523
  _emnapi_tsfn_send(func);
525
524
  pthread_mutex_unlock(&func->mutex);
@@ -584,7 +583,7 @@ napi_release_threadsafe_function(napi_threadsafe_function func,
584
583
  }
585
584
 
586
585
  napi_status
587
- napi_unref_threadsafe_function(napi_env env, napi_threadsafe_function func) {
586
+ napi_unref_threadsafe_function(node_api_basic_env env, napi_threadsafe_function func) {
588
587
  #if EMNAPI_HAVE_THREADS
589
588
  if (func->async_ref) {
590
589
  EMNAPI_KEEPALIVE_POP();
@@ -598,7 +597,7 @@ napi_unref_threadsafe_function(napi_env env, napi_threadsafe_function func) {
598
597
  }
599
598
 
600
599
  napi_status
601
- napi_ref_threadsafe_function(napi_env env, napi_threadsafe_function func) {
600
+ napi_ref_threadsafe_function(node_api_basic_env env, napi_threadsafe_function func) {
602
601
  #if EMNAPI_HAVE_THREADS
603
602
  if (!func->async_ref) {
604
603
  EMNAPI_KEEPALIVE_PUSH();
package/src/uv/queue.h CHANGED
@@ -18,91 +18,73 @@
18
18
 
19
19
  #include <stddef.h>
20
20
 
21
- typedef void *QUEUE[2];
22
-
23
- /* Private macros. */
24
- #define QUEUE_NEXT(q) (*(QUEUE **) &((*(q))[0]))
25
- #define QUEUE_PREV(q) (*(QUEUE **) &((*(q))[1]))
26
- #define QUEUE_PREV_NEXT(q) (QUEUE_NEXT(QUEUE_PREV(q)))
27
- #define QUEUE_NEXT_PREV(q) (QUEUE_PREV(QUEUE_NEXT(q)))
28
-
29
- /* Public macros. */
30
- #define QUEUE_DATA(ptr, type, field) \
31
- ((type *) ((char *) (ptr) - offsetof(type, field)))
32
-
33
- /* Important note: mutating the list while QUEUE_FOREACH is
34
- * iterating over its elements results in undefined behavior.
35
- */
36
- #define QUEUE_FOREACH(q, h) \
37
- for ((q) = QUEUE_NEXT(h); (q) != (h); (q) = QUEUE_NEXT(q))
38
-
39
- #define QUEUE_EMPTY(q) \
40
- ((const QUEUE *) (q) == (const QUEUE *) QUEUE_NEXT(q))
41
-
42
- #define QUEUE_HEAD(q) \
43
- (QUEUE_NEXT(q))
44
-
45
- #define QUEUE_INIT(q) \
46
- do { \
47
- QUEUE_NEXT(q) = (q); \
48
- QUEUE_PREV(q) = (q); \
49
- } \
50
- while (0)
51
-
52
- #define QUEUE_ADD(h, n) \
53
- do { \
54
- QUEUE_PREV_NEXT(h) = QUEUE_NEXT(n); \
55
- QUEUE_NEXT_PREV(n) = QUEUE_PREV(h); \
56
- QUEUE_PREV(h) = QUEUE_PREV(n); \
57
- QUEUE_PREV_NEXT(h) = (h); \
58
- } \
59
- while (0)
60
-
61
- #define QUEUE_SPLIT(h, q, n) \
62
- do { \
63
- QUEUE_PREV(n) = QUEUE_PREV(h); \
64
- QUEUE_PREV_NEXT(n) = (n); \
65
- QUEUE_NEXT(n) = (q); \
66
- QUEUE_PREV(h) = QUEUE_PREV(q); \
67
- QUEUE_PREV_NEXT(h) = (h); \
68
- QUEUE_PREV(q) = (n); \
69
- } \
70
- while (0)
71
-
72
- #define QUEUE_MOVE(h, n) \
73
- do { \
74
- if (QUEUE_EMPTY(h)) \
75
- QUEUE_INIT(n); \
76
- else { \
77
- QUEUE* q = QUEUE_HEAD(h); \
78
- QUEUE_SPLIT(h, q, n); \
79
- } \
80
- } \
81
- while (0)
82
-
83
- #define QUEUE_INSERT_HEAD(h, q) \
84
- do { \
85
- QUEUE_NEXT(q) = QUEUE_NEXT(h); \
86
- QUEUE_PREV(q) = (h); \
87
- QUEUE_NEXT_PREV(q) = (q); \
88
- QUEUE_NEXT(h) = (q); \
89
- } \
90
- while (0)
91
-
92
- #define QUEUE_INSERT_TAIL(h, q) \
93
- do { \
94
- QUEUE_NEXT(q) = (h); \
95
- QUEUE_PREV(q) = QUEUE_PREV(h); \
96
- QUEUE_PREV_NEXT(q) = (q); \
97
- QUEUE_PREV(h) = (q); \
98
- } \
99
- while (0)
100
-
101
- #define QUEUE_REMOVE(q) \
102
- do { \
103
- QUEUE_PREV_NEXT(q) = QUEUE_NEXT(q); \
104
- QUEUE_NEXT_PREV(q) = QUEUE_PREV(q); \
105
- } \
106
- while (0)
21
+ #define uv__queue_data(pointer, type, field) \
22
+ ((type*) ((char*) (pointer) - offsetof(type, field)))
23
+
24
+ #define uv__queue_foreach(q, h) \
25
+ for ((q) = (h)->next; (q) != (h); (q) = (q)->next)
26
+
27
+ static inline void uv__queue_init(struct uv__queue* q) {
28
+ q->next = q;
29
+ q->prev = q;
30
+ }
31
+
32
+ static inline int uv__queue_empty(const struct uv__queue* q) {
33
+ return q == q->next;
34
+ }
35
+
36
+ static inline struct uv__queue* uv__queue_head(const struct uv__queue* q) {
37
+ return q->next;
38
+ }
39
+
40
+ static inline struct uv__queue* uv__queue_next(const struct uv__queue* q) {
41
+ return q->next;
42
+ }
43
+
44
+ static inline void uv__queue_add(struct uv__queue* h, struct uv__queue* n) {
45
+ h->prev->next = n->next;
46
+ n->next->prev = h->prev;
47
+ h->prev = n->prev;
48
+ h->prev->next = h;
49
+ }
50
+
51
+ static inline void uv__queue_split(struct uv__queue* h,
52
+ struct uv__queue* q,
53
+ struct uv__queue* n) {
54
+ n->prev = h->prev;
55
+ n->prev->next = n;
56
+ n->next = q;
57
+ h->prev = q->prev;
58
+ h->prev->next = h;
59
+ q->prev = n;
60
+ }
61
+
62
+ static inline void uv__queue_move(struct uv__queue* h, struct uv__queue* n) {
63
+ if (uv__queue_empty(h))
64
+ uv__queue_init(n);
65
+ else
66
+ uv__queue_split(h, h->next, n);
67
+ }
68
+
69
+ static inline void uv__queue_insert_head(struct uv__queue* h,
70
+ struct uv__queue* q) {
71
+ q->next = h->next;
72
+ q->prev = h;
73
+ q->next->prev = q;
74
+ h->next = q;
75
+ }
76
+
77
+ static inline void uv__queue_insert_tail(struct uv__queue* h,
78
+ struct uv__queue* q) {
79
+ q->next = h;
80
+ q->prev = h->prev;
81
+ q->prev->next = q;
82
+ h->prev = q;
83
+ }
84
+
85
+ static inline void uv__queue_remove(struct uv__queue* q) {
86
+ q->prev->next = q->next;
87
+ q->next->prev = q->prev;
88
+ }
107
89
 
108
90
  #endif /* QUEUE_H_ */
@@ -19,7 +19,7 @@
19
19
  * IN THE SOFTWARE.
20
20
  */
21
21
 
22
- // from libuv 1.43.0
22
+ // from libuv 1.48.0
23
23
 
24
24
  #if defined(__EMSCRIPTEN_PTHREADS__) || defined(_REENTRANT)
25
25
 
@@ -43,10 +43,10 @@ static unsigned int slow_io_work_running;
43
43
  static unsigned int nthreads;
44
44
  static uv_thread_t* threads;
45
45
  static uv_thread_t default_threads[4];
46
- static QUEUE exit_message;
47
- static QUEUE wq;
48
- static QUEUE run_slow_work_message;
49
- static QUEUE slow_io_pending_wq;
46
+ static struct uv__queue exit_message;
47
+ static struct uv__queue wq;
48
+ static struct uv__queue run_slow_work_message;
49
+ static struct uv__queue slow_io_pending_wq;
50
50
 
51
51
  static unsigned int slow_work_thread_threshold(void) {
52
52
  return (nthreads + 1) / 2;
@@ -60,8 +60,8 @@ EMNAPI_INTERNAL_EXTERN void _emnapi_worker_unref(uv_thread_t pid);
60
60
 
61
61
  #ifdef __EMNAPI_WASI_THREADS__
62
62
  EMNAPI_INTERNAL_EXTERN
63
- void _emnapi_after_uvthreadpool_ready(void (*callback)(QUEUE* w, enum uv__work_kind kind),
64
- QUEUE* w,
63
+ void _emnapi_after_uvthreadpool_ready(void (*callback)(struct uv__queue* w, enum uv__work_kind kind),
64
+ struct uv__queue* w,
65
65
  enum uv__work_kind kind);
66
66
  EMNAPI_INTERNAL_EXTERN void _emnapi_tell_js_uvthreadpool(uv_thread_t* threads, unsigned int n);
67
67
  EMNAPI_INTERNAL_EXTERN void _emnapi_emit_async_thread_ready();
@@ -72,7 +72,7 @@ EMNAPI_INTERNAL_EXTERN void _emnapi_emit_async_thread_ready();
72
72
  */
73
73
  static void* worker(void* arg) {
74
74
  struct uv__work* w;
75
- QUEUE* q;
75
+ struct uv__queue* q;
76
76
  int is_slow_work;
77
77
  #ifndef __EMNAPI_WASI_THREADS__
78
78
  uv_sem_post((uv_sem_t*) arg);
@@ -87,49 +87,49 @@ static void* worker(void* arg) {
87
87
 
88
88
  /* Keep waiting while either no work is present or only slow I/O
89
89
  and we're at the threshold for that. */
90
- while (QUEUE_EMPTY(&wq) ||
91
- (QUEUE_HEAD(&wq) == &run_slow_work_message &&
92
- QUEUE_NEXT(&run_slow_work_message) == &wq &&
90
+ while (uv__queue_empty(&wq) ||
91
+ (uv__queue_head(&wq) == &run_slow_work_message &&
92
+ uv__queue_next(&run_slow_work_message) == &wq &&
93
93
  slow_io_work_running >= slow_work_thread_threshold())) {
94
94
  idle_threads += 1;
95
95
  uv_cond_wait(&cond, &mutex);
96
96
  idle_threads -= 1;
97
97
  }
98
98
 
99
- q = QUEUE_HEAD(&wq);
99
+ q = uv__queue_head(&wq);
100
100
  if (q == &exit_message) {
101
101
  uv_cond_signal(&cond);
102
102
  uv_mutex_unlock(&mutex);
103
103
  break;
104
104
  }
105
105
 
106
- QUEUE_REMOVE(q);
107
- QUEUE_INIT(q); /* Signal uv_cancel() that the work req is executing. */
106
+ uv__queue_remove(q);
107
+ uv__queue_init(q); /* Signal uv_cancel() that the work req is executing. */
108
108
 
109
109
  is_slow_work = 0;
110
110
  if (q == &run_slow_work_message) {
111
111
  /* If we're at the slow I/O threshold, re-schedule until after all
112
112
  other work in the queue is done. */
113
113
  if (slow_io_work_running >= slow_work_thread_threshold()) {
114
- QUEUE_INSERT_TAIL(&wq, q);
114
+ uv__queue_insert_tail(&wq, q);
115
115
  continue;
116
116
  }
117
117
 
118
118
  /* If we encountered a request to run slow I/O work but there is none
119
119
  to run, that means it's cancelled => Start over. */
120
- if (QUEUE_EMPTY(&slow_io_pending_wq))
120
+ if (uv__queue_empty(&slow_io_pending_wq))
121
121
  continue;
122
122
 
123
123
  is_slow_work = 1;
124
124
  slow_io_work_running++;
125
125
 
126
- q = QUEUE_HEAD(&slow_io_pending_wq);
127
- QUEUE_REMOVE(q);
128
- QUEUE_INIT(q);
126
+ q = uv__queue_head(&slow_io_pending_wq);
127
+ uv__queue_remove(q);
128
+ uv__queue_init(q);
129
129
 
130
130
  /* If there is more slow I/O work, schedule it to be run as well. */
131
- if (!QUEUE_EMPTY(&slow_io_pending_wq)) {
132
- QUEUE_INSERT_TAIL(&wq, &run_slow_work_message);
131
+ if (!uv__queue_empty(&slow_io_pending_wq)) {
132
+ uv__queue_insert_tail(&wq, &run_slow_work_message);
133
133
  if (idle_threads > 0)
134
134
  uv_cond_signal(&cond);
135
135
  }
@@ -137,13 +137,13 @@ static void* worker(void* arg) {
137
137
 
138
138
  uv_mutex_unlock(&mutex);
139
139
 
140
- w = QUEUE_DATA(q, struct uv__work, wq);
140
+ w = uv__queue_data(q, struct uv__work, wq);
141
141
  w->work(w);
142
142
 
143
143
  uv_mutex_lock(&w->loop->wq_mutex);
144
144
  w->work = NULL; /* Signal uv_cancel() that the work req is done
145
145
  executing. */
146
- QUEUE_INSERT_TAIL(&w->loop->wq, &w->wq);
146
+ uv__queue_insert_tail(&w->loop->wq, &w->wq);
147
147
  uv_async_send(&w->loop->wq_async);
148
148
  uv_mutex_unlock(&w->loop->wq_mutex);
149
149
 
@@ -159,12 +159,12 @@ static void* worker(void* arg) {
159
159
  }
160
160
 
161
161
 
162
- static void post(QUEUE* q, enum uv__work_kind kind) {
162
+ static void post(struct uv__queue* q, enum uv__work_kind kind) {
163
163
  uv_mutex_lock(&mutex);
164
164
  // if (kind == UV__WORK_SLOW_IO) {
165
165
  // /* Insert into a separate queue. */
166
- // QUEUE_INSERT_TAIL(&slow_io_pending_wq, q);
167
- // if (!QUEUE_EMPTY(&run_slow_work_message)) {
166
+ // uv__queue_insert_tail(&slow_io_pending_wq, q);
167
+ // if (!uv__queue_empty(&run_slow_work_message)) {
168
168
  // /* Running slow I/O tasks is already scheduled => Nothing to do here.
169
169
  // The worker that runs said other task will schedule this one as well. */
170
170
  // uv_mutex_unlock(&mutex);
@@ -173,7 +173,7 @@ static void post(QUEUE* q, enum uv__work_kind kind) {
173
173
  // q = &run_slow_work_message;
174
174
  // }
175
175
 
176
- QUEUE_INSERT_TAIL(&wq, q);
176
+ uv__queue_insert_tail(&wq, q);
177
177
  if (idle_threads > 0)
178
178
  uv_cond_signal(&cond);
179
179
  uv_mutex_unlock(&mutex);
@@ -254,9 +254,9 @@ static void init_threads(void) {
254
254
  if (uv_mutex_init(&mutex))
255
255
  abort();
256
256
 
257
- QUEUE_INIT(&wq);
258
- QUEUE_INIT(&slow_io_pending_wq);
259
- QUEUE_INIT(&run_slow_work_message);
257
+ uv__queue_init(&wq);
258
+ uv__queue_init(&slow_io_pending_wq);
259
+ uv__queue_init(&run_slow_work_message);
260
260
 
261
261
  #ifndef __EMNAPI_WASI_THREADS__
262
262
  if (uv_sem_init(&sem, 0))
@@ -331,12 +331,13 @@ void uv__work_submit(uv_loop_t* loop,
331
331
  static int uv__work_cancel(uv_loop_t* loop, uv_req_t* req, struct uv__work* w) {
332
332
  int cancelled;
333
333
 
334
+ uv_once(&once, init_once); /* Ensure |mutex| is initialized. */
334
335
  uv_mutex_lock(&mutex);
335
336
  uv_mutex_lock(&w->loop->wq_mutex);
336
337
 
337
- cancelled = !QUEUE_EMPTY(&w->wq) && w->work != NULL;
338
+ cancelled = !uv__queue_empty(&w->wq) && w->work != NULL;
338
339
  if (cancelled)
339
- QUEUE_REMOVE(&w->wq);
340
+ uv__queue_remove(&w->wq);
340
341
 
341
342
  uv_mutex_unlock(&w->loop->wq_mutex);
342
343
  uv_mutex_unlock(&mutex);
@@ -346,7 +347,7 @@ static int uv__work_cancel(uv_loop_t* loop, uv_req_t* req, struct uv__work* w) {
346
347
 
347
348
  w->work = uv__cancelled;
348
349
  uv_mutex_lock(&loop->wq_mutex);
349
- QUEUE_INSERT_TAIL(&loop->wq, &w->wq);
350
+ uv__queue_insert_tail(&loop->wq, &w->wq);
350
351
  uv_async_send(&loop->wq_async);
351
352
  uv_mutex_unlock(&loop->wq_mutex);
352
353
 
@@ -357,22 +358,39 @@ static int uv__work_cancel(uv_loop_t* loop, uv_req_t* req, struct uv__work* w) {
357
358
  void uv__work_done(uv_async_t* handle) {
358
359
  struct uv__work* w;
359
360
  uv_loop_t* loop;
360
- QUEUE* q;
361
- QUEUE wq;
361
+ struct uv__queue* q;
362
+ struct uv__queue wq;
362
363
  int err;
364
+ int nevents;
363
365
 
364
366
  loop = container_of(handle, uv_loop_t, wq_async);
365
367
  uv_mutex_lock(&loop->wq_mutex);
366
- QUEUE_MOVE(&loop->wq, &wq);
368
+ uv__queue_move(&loop->wq, &wq);
367
369
  uv_mutex_unlock(&loop->wq_mutex);
368
370
 
369
- while (!QUEUE_EMPTY(&wq)) {
370
- q = QUEUE_HEAD(&wq);
371
- QUEUE_REMOVE(q);
371
+ nevents = 0;
372
+
373
+ while (!uv__queue_empty(&wq)) {
374
+ q = uv__queue_head(&wq);
375
+ uv__queue_remove(q);
372
376
 
373
377
  w = container_of(q, struct uv__work, wq);
374
378
  err = (w->work == uv__cancelled) ? ECANCELED : 0;
375
379
  w->done(w, err);
380
+ nevents++;
381
+ }
382
+
383
+ /* This check accomplishes 2 things:
384
+ * 1. Even if the queue was empty, the call to uv__work_done() should count
385
+ * as an event. Which will have been added by the event loop when
386
+ * calling this callback.
387
+ * 2. Prevents accidental wrap around in case nevents == 0 events == 0.
388
+ */
389
+ if (nevents > 1) {
390
+ /* Subtract 1 to counter the call to uv__work_done(). */
391
+ uv__metrics_inc_events(loop, nevents - 1);
392
+ if (uv__get_internal_fields(loop)->current_timeout == 0)
393
+ uv__metrics_inc_events_waiting(loop, nevents - 1);
376
394
  }
377
395
  }
378
396
 
@@ -388,7 +406,7 @@ static void uv__queue_done(struct uv__work* w, int err) {
388
406
  uv_work_t* req;
389
407
 
390
408
  req = container_of(w, uv_work_t, work_req);
391
- uv__req_unregister(req->loop, req);
409
+ uv__req_unregister(req->loop);
392
410
 
393
411
  if (req->after_work_cb == NULL)
394
412
  return;