erlang各种有用的函数包括一些有用nif封装,还有一些性能测试case。
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

441 lines
11 KiB

5 years ago
  1. #define _GNU_SOURCE
  2. #include "erl_nif.h"
  3. #include <assert.h>
  4. #include <stdio.h>
  5. #include <stdint.h>
  6. #include <string.h>
  7. #include <time.h>
  8. // #include "fifo.h"
  9. #include "lifo.h"
  10. typedef struct {
  11. ERL_NIF_TERM ok;
  12. ERL_NIF_TERM error;
  13. ERL_NIF_TERM fifo;
  14. ERL_NIF_TERM lifo;
  15. ERL_NIF_TERM ttl;
  16. ERL_NIF_TERM max_size;
  17. } atoms_t;
  18. typedef struct {
  19. ErlNifResourceType *queue;
  20. atoms_t atoms;
  21. } priv_t;
  22. typedef struct {
  23. union {
  24. fifo_handle_t fifo;
  25. lifo_handle_t lifo;
  26. } handle;
  27. ErlNifBinary data;
  28. struct timespec added;
  29. } item_t;
  30. typedef enum {
  31. QTYPE_FIFO = 0,
  32. QTYPE_LIFO
  33. } queue_type_t;
  34. typedef struct queue {
  35. union {
  36. fifo_t fifo;
  37. lifo_t lifo;
  38. } queue;
  39. uint64_t ttl;
  40. uint64_t max_size;
  41. void (*push) (struct queue *inst, item_t *item);
  42. item_t* (*pop) (struct queue *inst);
  43. void (*free) (struct queue *inst);
  44. uint64_t (*size) (struct queue *inst);
  45. void (*cleanup) (struct queue *inst);
  46. } queue_t;
  47. // returns tuple {error, atom()}
  48. static inline ERL_NIF_TERM
  49. make_error(ErlNifEnv* env, const char *error) {
  50. priv_t *priv = (priv_t *) enif_priv_data(env);
  51. return enif_make_tuple2(env, priv->atoms.error, enif_make_atom(env, error));
  52. }
  53. // returns time diff in milliseconds
  54. static inline int64_t
  55. tdiff(struct timespec *t2, struct timespec *t1) {
  56. return (t2->tv_sec * 1000 + t2->tv_nsec / 1000000UL) -
  57. (t1->tv_sec * 1000 + t1->tv_nsec / 1000000UL);
  58. }
  59. static inline void
  60. gettime(struct timespec *tp) {
  61. int rc = clock_gettime(CLOCK_MONOTONIC_RAW, tp);
  62. assert(rc == 0);
  63. }
  64. /******************************************************************************/
  65. /* FIFO callbacks */
  66. /******************************************************************************/
  67. static void
  68. cleanup_fifo(queue_t *inst) {
  69. struct timespec now;
  70. gettime(&now);
  71. for (;;) {
  72. item_t *item = NULL;
  73. __fifo_peak(&inst->queue.fifo, item, handle.fifo);
  74. if (item == NULL)
  75. return;
  76. int64_t diff = tdiff(&now, &item->added);
  77. if (diff < inst->ttl) {
  78. return;
  79. } else {
  80. __fifo_pop(&inst->queue.fifo, item, handle.fifo);
  81. enif_release_binary(&item->data);
  82. enif_free(item);
  83. }
  84. }
  85. }
  86. static void
  87. push_fifo(queue_t *inst, item_t *item) {
  88. __fifo_push(&inst->queue.fifo, item, handle.fifo);
  89. }
  90. static item_t *
  91. pop_fifo(queue_t *inst) {
  92. item_t *item = NULL;
  93. if (inst->ttl > 0) {
  94. struct timespec now;
  95. gettime(&now);
  96. for (;;) {
  97. __fifo_pop(&inst->queue.fifo, item, handle.fifo);
  98. if (item == NULL)
  99. return NULL;
  100. int64_t diff = tdiff(&now, &item->added);
  101. if (diff < inst->ttl) {
  102. return item;
  103. } else {
  104. enif_release_binary(&item->data);
  105. enif_free(item);
  106. }
  107. }
  108. } else {
  109. __fifo_pop(&inst->queue.fifo, item, handle.fifo);
  110. }
  111. return item;
  112. }
  113. static void
  114. free_fifo(queue_t *inst) {
  115. item_t *item;
  116. for(;;) {
  117. __fifo_pop(&inst->queue.fifo, item, handle.fifo);
  118. if (item == NULL)
  119. return;
  120. enif_release_binary(&item->data);
  121. enif_free(item);
  122. }
  123. }
  124. static uint64_t
  125. size_fifo(queue_t *inst) {
  126. return fifo_length(&inst->queue.fifo);
  127. }
  128. /******************************************************************************/
  129. /* LIFO callbacks */
  130. /******************************************************************************/
  131. static void
  132. cleanup_lifo(queue_t *inst) {
  133. struct timespec now;
  134. gettime(&now);
  135. for(;;) {
  136. item_t *item = inst->queue.lifo.tail;
  137. if (item == NULL)
  138. return;
  139. int64_t diff = tdiff(&now, &item->added);
  140. if (diff < inst->ttl) {
  141. return;
  142. } else {
  143. item_t *prev = item->handle.lifo.prev;
  144. if (prev != NULL)
  145. prev->handle.lifo.next = NULL;
  146. inst->queue.lifo.tail = prev;
  147. enif_release_binary(&item->data);
  148. enif_free(item);
  149. }
  150. }
  151. }
  152. static void
  153. push_lifo(queue_t *inst, item_t *item) {
  154. __lifo_push(&inst->queue.lifo, item, handle.lifo);
  155. }
  156. static item_t *
  157. pop_lifo(queue_t *inst) {
  158. item_t *item = NULL;
  159. if (inst->ttl > 0)
  160. cleanup_lifo(inst);
  161. __lifo_pop(&inst->queue.lifo, item, handle.lifo);
  162. return item;
  163. }
  164. static void
  165. free_lifo(queue_t *inst) {
  166. item_t *item;
  167. for(;;) {
  168. __lifo_pop(&inst->queue.lifo, item, handle.lifo);
  169. if (item == NULL)
  170. return;
  171. enif_release_binary(&item->data);
  172. enif_free(item);
  173. }
  174. }
  175. static uint64_t
  176. size_lifo(queue_t *inst) {
  177. return lifo_length(&inst->queue.lifo);
  178. }
  179. /******************************************************************************
  180. ** NIFs
  181. *******************************************************************************/
  182. static ERL_NIF_TERM
  183. new_queue(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {
  184. if (!enif_is_list(env, argv[0]))
  185. return enif_make_badarg(env);
  186. priv_t *priv = (priv_t *) enif_priv_data(env);
  187. queue_type_t qtype = QTYPE_FIFO;
  188. unsigned long ttl = 0;
  189. unsigned long max_size = 0;
  190. ERL_NIF_TERM settings_list = argv[0];
  191. ERL_NIF_TERM head;
  192. // parses proplist [fifo, lifo, {ttl, non_neg_integer()}, {max_size, non_neg_integer()}]
  193. while(enif_get_list_cell(env, settings_list, &head, &settings_list))
  194. {
  195. const ERL_NIF_TERM *items;
  196. int arity;
  197. if (enif_is_atom(env, head)) {
  198. if (enif_is_identical(head, priv->atoms.fifo)) {
  199. qtype = QTYPE_FIFO;
  200. } else if (enif_is_identical(head, priv->atoms.lifo)) {
  201. qtype = QTYPE_LIFO;
  202. } else {
  203. return enif_make_badarg(env);
  204. }
  205. } else if (enif_get_tuple(env, head, &arity, &items) && arity == 2) {
  206. if (enif_is_identical(items[0], priv->atoms.ttl)) {
  207. if (!enif_get_ulong(env, items[1], &ttl)) {
  208. return enif_make_badarg(env);
  209. }
  210. } else if (enif_is_identical(items[0], priv->atoms.max_size)) {
  211. if (!enif_get_ulong(env, items[1], &max_size)) {
  212. return enif_make_badarg(env);
  213. }
  214. } else {
  215. return enif_make_badarg(env);
  216. }
  217. } else {
  218. return enif_make_badarg(env);
  219. }
  220. }
  221. queue_t *inst = (queue_t *) enif_alloc_resource(priv->queue, sizeof(*inst));
  222. if (inst == NULL)
  223. return make_error(env, "enif_alloc_resource");
  224. inst->ttl = ttl;
  225. inst->max_size = max_size;
  226. switch (qtype) {
  227. case QTYPE_FIFO:
  228. fifo_init(&inst->queue.fifo);
  229. inst->push = &push_fifo;
  230. inst->pop = &pop_fifo;
  231. inst->free = &free_fifo;
  232. inst->size = &size_fifo;
  233. inst->cleanup = &cleanup_fifo;
  234. break;
  235. case QTYPE_LIFO:
  236. lifo_init(&inst->queue.lifo);
  237. inst->push = &push_lifo;
  238. inst->pop = &pop_lifo;
  239. inst->free = &free_lifo;
  240. inst->size = &size_lifo;
  241. inst->cleanup = &cleanup_lifo;
  242. break;
  243. }
  244. ERL_NIF_TERM result = enif_make_resource(env, inst);
  245. enif_release_resource(inst);
  246. return enif_make_tuple2(env, priv->atoms.ok, result);
  247. }
  248. static ERL_NIF_TERM
  249. push_item(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {
  250. priv_t *priv = (priv_t *) enif_priv_data(env);
  251. queue_t *inst;
  252. if (!enif_get_resource(env, argv[0], priv->queue, (void**) &inst))
  253. return enif_make_badarg(env);
  254. // todo: check an owner of the queue
  255. ErlNifBinary bin;
  256. if (!enif_inspect_binary(env, argv[1], &bin))
  257. return enif_make_badarg(env);
  258. if (inst->ttl > 0) {
  259. inst->cleanup(inst);
  260. }
  261. if (inst->max_size > 0 && inst->size(inst) >= inst->max_size) {
  262. return enif_make_tuple2(env, priv->atoms.error, priv->atoms.max_size);
  263. }
  264. item_t *item = (item_t *) enif_alloc(sizeof(*item));
  265. if (item == NULL)
  266. return make_error(env, "enif_alloc");
  267. if (!enif_alloc_binary(bin.size, &item->data)) {
  268. enif_free(item);
  269. return make_error(env, "enif_alloc_binary");
  270. }
  271. memcpy(item->data.data, bin.data, bin.size);
  272. if (inst->ttl > 0) {
  273. gettime(&item->added);
  274. }
  275. inst->push(inst, item);
  276. return priv->atoms.ok;
  277. }
  278. static ERL_NIF_TERM
  279. pop_item(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {
  280. priv_t *priv = (priv_t *) enif_priv_data(env);
  281. queue_t *inst;
  282. item_t *item;
  283. if (!enif_get_resource(env, argv[0], priv->queue, (void**) &inst))
  284. return enif_make_badarg(env);
  285. // todo: check an owner of the queue
  286. item = inst->pop(inst);
  287. if (item == NULL)
  288. return enif_make_list(env, 0);
  289. ERL_NIF_TERM result = enif_make_binary(env, &item->data);
  290. enif_free(item);
  291. return enif_make_list1(env, result);
  292. }
  293. static ERL_NIF_TERM
  294. queue_size(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {
  295. priv_t *priv = (priv_t *) enif_priv_data(env);
  296. queue_t *inst;
  297. if (!enif_get_resource(env, argv[0], priv->queue, (void**) &inst))
  298. return enif_make_badarg(env);
  299. return enif_make_uint64(env, inst->size(inst));
  300. }
  301. /******************************************************************************
  302. ** NIF initialization
  303. *******************************************************************************/
  304. static void
  305. enq_queue_free(ErlNifEnv* env, void* obj) {
  306. queue_t *inst = obj;
  307. inst->free(inst);
  308. }
  309. static priv_t *
  310. make_priv(ErlNifEnv *env) {
  311. priv_t *priv = enif_alloc(sizeof(*priv));
  312. if (priv == NULL)
  313. return NULL;
  314. ErlNifResourceFlags flags = ERL_NIF_RT_CREATE | ERL_NIF_RT_TAKEOVER;
  315. priv->queue = enif_open_resource_type(env, NULL, "enq_queue", enq_queue_free, flags, NULL);
  316. priv->atoms.ok = enif_make_atom(env, "ok");
  317. priv->atoms.error = enif_make_atom(env, "error");
  318. priv->atoms.fifo = enif_make_atom(env, "fifo");
  319. priv->atoms.lifo = enif_make_atom(env, "lifo");
  320. priv->atoms.ttl = enif_make_atom(env, "ttl");
  321. priv->atoms.max_size = enif_make_atom(env, "max_size");
  322. return priv;
  323. }
  324. static int
  325. enq_nif_load(ErlNifEnv *env, void **priv_data, ERL_NIF_TERM load_info) {
  326. *priv_data = make_priv(env);
  327. return 0;
  328. }
  329. static int
  330. enq_nif_upgrade(ErlNifEnv *env, void **priv_data, void **old_priv_data, ERL_NIF_TERM load_info) {
  331. *priv_data = make_priv(env);
  332. return 0;
  333. }
  334. static ErlNifFunc enq_nif_funcs[] = {
  335. {"new", 1, new_queue},
  336. {"push", 2, push_item},
  337. {"pop", 1, pop_item},
  338. {"size", 1, queue_size},
  339. };
  340. ERL_NIF_INIT(enq_nif, enq_nif_funcs, enq_nif_load, NULL, enq_nif_upgrade, NULL)