File Coverage

Shared.xs
Criterion Covered Total %
statement 108 113 95.5
branch 121 226 53.5
condition n/a
subroutine n/a
pod n/a
total 229 339 67.5


line stmt bran cond sub pod time code
1             #define PERL_NO_GET_CONTEXT
2             #include "EXTERN.h"
3             #include "perl.h"
4             #include "XSUB.h"
5             #include "ppport.h"
6             #include "graph.h"
7              
8             #define EXTRACT_GRAPH(sv) \
9             if (!sv_isobject(sv) || !sv_derived_from(sv, "Data::Graph::Shared")) \
10             croak("Expected a Data::Graph::Shared object"); \
11             GraphHandle *h = INT2PTR(GraphHandle*, SvIV(SvRV(sv))); \
12             if (!h) croak("Attempted to use a destroyed Data::Graph::Shared object")
13              
14             #define MAKE_OBJ(class, handle) \
15             SV *obj = newSViv(PTR2IV(handle)); \
16             SV *ref = newRV_noinc(obj); \
17             sv_bless(ref, gv_stashpv(class, GV_ADD)); \
18             RETVAL = ref
19              
20             #define REQUIRE_NODE(h, node) do { \
21             if ((uint32_t)(node) >= (h)->hdr->max_nodes || !graph_bit_set((h)->node_bitmap, (uint32_t)(node))) { \
22             graph_mutex_unlock((h)->hdr); \
23             croak("node %u does not exist", (unsigned)(node)); \
24             } \
25             } while (0)
26              
27             MODULE = Data::Graph::Shared PACKAGE = Data::Graph::Shared
28              
29             PROTOTYPES: DISABLE
30              
31             SV *
32             new(class, path, max_nodes, max_edges)
33             const char *class
34             SV *path
35             UV max_nodes
36             UV max_edges
37             PREINIT:
38             char errbuf[GRAPH_ERR_BUFLEN];
39             CODE:
40 10 100         const char *p = SvOK(path) ? SvPV_nolen(path) : NULL;
41 10           GraphHandle *h = graph_create(p, (uint32_t)max_nodes, (uint32_t)max_edges, errbuf);
42 10 50         if (!h) croak("Data::Graph::Shared->new: %s", errbuf);
43 10           MAKE_OBJ(class, h);
44             OUTPUT:
45             RETVAL
46              
47             SV *
48             new_memfd(class, name, max_nodes, max_edges)
49             const char *class
50             const char *name
51             UV max_nodes
52             UV max_edges
53             PREINIT:
54             char errbuf[GRAPH_ERR_BUFLEN];
55             CODE:
56 2           GraphHandle *h = graph_create_memfd(name, (uint32_t)max_nodes, (uint32_t)max_edges, errbuf);
57 2 50         if (!h) croak("Data::Graph::Shared->new_memfd: %s", errbuf);
58 2           MAKE_OBJ(class, h);
59             OUTPUT:
60             RETVAL
61              
62             SV *
63             new_from_fd(class, fd)
64             const char *class
65             int fd
66             PREINIT:
67             char errbuf[GRAPH_ERR_BUFLEN];
68             CODE:
69 1           GraphHandle *h = graph_open_fd(fd, errbuf);
70 1 50         if (!h) croak("Data::Graph::Shared->new_from_fd: %s", errbuf);
71 1           MAKE_OBJ(class, h);
72             OUTPUT:
73             RETVAL
74              
75             void
76             DESTROY(self)
77             SV *self
78             CODE:
79 13 50         if (!SvROK(self)) return;
80 13           GraphHandle *h = INT2PTR(GraphHandle*, SvIV(SvRV(self)));
81 13 50         if (!h) return;
82 13           sv_setiv(SvRV(self), 0);
83 13           graph_destroy(h);
84              
85             void
86             sync(self)
87             SV *self
88             PREINIT:
89 1 50         EXTRACT_GRAPH(self);
    50          
    50          
90             CODE:
91 1 50         if (graph_msync(h) != 0) croak("msync: %s", strerror(errno));
92              
93             void
94             unlink(self_or_class, ...)
95             SV *self_or_class
96             CODE:
97 1           const char *p = NULL;
98 1 50         if (sv_isobject(self_or_class)) {
99 1           GraphHandle *h = INT2PTR(GraphHandle*, SvIV(SvRV(self_or_class)));
100 1 50         if (!h) croak("Attempted to use a destroyed object");
101 1           p = h->path;
102             } else {
103 0 0         if (items < 2) croak("Usage: ...->unlink($path)");
104 0           p = SvPV_nolen(ST(1));
105             }
106 1 50         if (!p) croak("cannot unlink anonymous or memfd object");
107 1 50         if (unlink(p) != 0) croak("unlink(%s): %s", p, strerror(errno));
108              
109             IV
110             memfd(self)
111             SV *self
112             PREINIT:
113 2 50         EXTRACT_GRAPH(self);
    50          
    50          
114             CODE:
115 2 50         RETVAL = h->backing_fd;
116             OUTPUT:
117             RETVAL
118              
119             IV
120             eventfd(self)
121             SV *self
122             PREINIT:
123 2 50         EXTRACT_GRAPH(self);
    50          
    50          
124             CODE:
125 2           RETVAL = graph_create_eventfd(h);
126 2 50         if (RETVAL < 0) croak("eventfd: %s", strerror(errno));
127             OUTPUT:
128             RETVAL
129              
130             void
131             eventfd_set(self, fd)
132             SV *self
133             int fd
134             PREINIT:
135 3 50         EXTRACT_GRAPH(self);
    50          
    50          
136             CODE:
137 3 50         if (h->notify_fd >= 0 && h->notify_fd != fd) close(h->notify_fd);
    100          
138 3           h->notify_fd = fd;
139              
140             IV
141             fileno(self)
142             SV *self
143             PREINIT:
144 5 50         EXTRACT_GRAPH(self);
    50          
    50          
145             CODE:
146 5 50         RETVAL = h->notify_fd;
147             OUTPUT:
148             RETVAL
149              
150             bool
151             notify(self)
152             SV *self
153             PREINIT:
154 3 50         EXTRACT_GRAPH(self);
    50          
    50          
155             CODE:
156 3 50         RETVAL = graph_notify(h);
157             OUTPUT:
158             RETVAL
159              
160             SV *
161             eventfd_consume(self)
162             SV *self
163             PREINIT:
164 2 50         EXTRACT_GRAPH(self);
    50          
    50          
165             CODE:
166 2           int64_t n = graph_eventfd_consume(h);
167 2 50         RETVAL = (n >= 0) ? newSViv((IV)n) : &PL_sv_undef;
168             OUTPUT:
169             RETVAL
170              
171             SV *
172             add_node(self, data)
173             SV *self
174             IV data
175             PREINIT:
176 80 50         EXTRACT_GRAPH(self);
    50          
    50          
177             CODE:
178 80           int32_t idx = graph_add_node(h, (int64_t)data);
179 80 50         RETVAL = (idx >= 0) ? newSViv((IV)idx) : &PL_sv_undef;
180             OUTPUT:
181             RETVAL
182              
183             bool
184             add_edge(self, src, dst, ...)
185             SV *self
186             UV src
187             UV dst
188             PREINIT:
189 11 50         EXTRACT_GRAPH(self);
    50          
    50          
190             CODE:
191 11 100         int64_t weight = (items > 3) ? (int64_t)SvIV(ST(3)) : 1;
192 11 100         RETVAL = graph_add_edge(h, (uint32_t)src, (uint32_t)dst, weight);
193             OUTPUT:
194             RETVAL
195              
196             bool
197             remove_node(self, node)
198             SV *self
199             UV node
200             PREINIT:
201 2 50         EXTRACT_GRAPH(self);
    50          
    50          
202             CODE:
203 2 50         RETVAL = graph_remove_node(h, (uint32_t)node);
204             OUTPUT:
205             RETVAL
206              
207             bool
208             remove_node_full(self, node)
209             SV *self
210             UV node
211             PREINIT:
212 4 50         EXTRACT_GRAPH(self);
    50          
    50          
213             CODE:
214 4 100         RETVAL = graph_remove_node_full(h, (uint32_t)node);
215             OUTPUT:
216             RETVAL
217              
218             bool
219             has_node(self, node)
220             SV *self
221             UV node
222             PREINIT:
223 22 50         EXTRACT_GRAPH(self);
    50          
    50          
224             CODE:
225 22 100         RETVAL = graph_has_node(h, (uint32_t)node);
226             OUTPUT:
227             RETVAL
228              
229             IV
230             node_data(self, node)
231             SV *self
232             UV node
233             PREINIT:
234 3 50         EXTRACT_GRAPH(self);
    50          
    50          
235             CODE:
236 3           graph_mutex_lock(h->hdr);
237 3 50         REQUIRE_NODE(h, node);
    50          
238 3           RETVAL = (IV)h->node_data[(uint32_t)node];
239 3           graph_mutex_unlock(h->hdr);
240             OUTPUT:
241             RETVAL
242              
243             void
244             set_node_data(self, node, data)
245             SV *self
246             UV node
247             IV data
248             PREINIT:
249 2 50         EXTRACT_GRAPH(self);
    50          
    50          
250             CODE:
251 2           graph_mutex_lock(h->hdr);
252 2 50         REQUIRE_NODE(h, node);
    50          
253 2           h->node_data[(uint32_t)node] = (int64_t)data;
254 2           __atomic_fetch_add(&h->hdr->stat_ops, 1, __ATOMIC_RELAXED);
255 2           graph_mutex_unlock(h->hdr);
256              
257             void
258             neighbors(self, node)
259             SV *self
260             UV node
261             PREINIT:
262 4 50         EXTRACT_GRAPH(self);
    50          
    50          
263             PPCODE:
264             /* Collect edges under lock, then build Perl SVs outside it:
265             * newAV/newSVuv/newSViv can longjmp on OOM, which would leak the
266             * process-shared mutex to peers (no automatic cleanup for futex). */
267 4           graph_mutex_lock(h->hdr);
268 4 50         REQUIRE_NODE(h, node);
    50          
269 4           uint32_t deg = graph_degree(h, (uint32_t)node);
270 4           uint32_t eidx = h->node_heads[(uint32_t)node];
271 4 50         uint32_t *dsts = deg ? (uint32_t *)malloc(deg * sizeof(uint32_t)) : NULL;
272 4 50         int64_t *wts = deg ? (int64_t *)malloc(deg * sizeof(int64_t)) : NULL;
273 4 50         if (deg && (!dsts || !wts)) {
    50          
    50          
274 0           free(dsts); free(wts);
275 0           graph_mutex_unlock(h->hdr);
276 0           croak("neighbors: out of memory");
277             }
278 4           uint32_t i = 0;
279 12 100         while (eidx != GRAPH_NONE && i < deg) {
    50          
280 8           dsts[i] = h->edges[eidx].dst;
281 8           wts[i] = h->edges[eidx].weight;
282 8           eidx = h->edges[eidx].next;
283 8           i++;
284             }
285 4           graph_mutex_unlock(h->hdr);
286 4 50         EXTEND(SP, (SSize_t)i);
287 12 100         for (uint32_t j = 0; j < i; j++) {
288 8           AV *pair = newAV();
289 8           av_push(pair, newSVuv(dsts[j]));
290 8           av_push(pair, newSViv((IV)wts[j]));
291 8           PUSHs(sv_2mortal(newRV_noinc((SV *)pair)));
292             }
293 4           free(dsts); free(wts);
294              
295             UV
296             degree(self, node)
297             SV *self
298             UV node
299             PREINIT:
300 4 50         EXTRACT_GRAPH(self);
    50          
    50          
301             CODE:
302 4           graph_mutex_lock(h->hdr);
303 4 50         REQUIRE_NODE(h, node);
    50          
304 4           RETVAL = graph_degree(h, (uint32_t)node);
305 4           graph_mutex_unlock(h->hdr);
306             OUTPUT:
307             RETVAL
308              
309             UV
310             node_count(self)
311             SV *self
312             PREINIT:
313 6 50         EXTRACT_GRAPH(self);
    50          
    50          
314             CODE:
315 6 50         RETVAL = __atomic_load_n(&h->hdr->node_count, __ATOMIC_ACQUIRE);
316             OUTPUT:
317             RETVAL
318              
319             UV
320             edge_count(self)
321             SV *self
322             PREINIT:
323 9 50         EXTRACT_GRAPH(self);
    50          
    50          
324             CODE:
325 9 50         RETVAL = __atomic_load_n(&h->hdr->edge_count, __ATOMIC_ACQUIRE);
326             OUTPUT:
327             RETVAL
328              
329             UV
330             max_nodes(self)
331             SV *self
332             PREINIT:
333 2 50         EXTRACT_GRAPH(self);
    50          
    50          
334             CODE:
335 2 50         RETVAL = h->hdr->max_nodes;
336             OUTPUT:
337             RETVAL
338              
339             UV
340             max_edges(self)
341             SV *self
342             PREINIT:
343 1 50         EXTRACT_GRAPH(self);
    50          
    50          
344             CODE:
345 1 50         RETVAL = h->hdr->max_edges;
346             OUTPUT:
347             RETVAL
348              
349             SV *
350             stats(self)
351             SV *self
352             PREINIT:
353 3 50         EXTRACT_GRAPH(self);
    50          
    50          
354             CODE:
355 3           HV *hv = newHV();
356 3           hv_store(hv, "node_count", 10, newSVuv(__atomic_load_n(&h->hdr->node_count, __ATOMIC_ACQUIRE)), 0);
357 3           hv_store(hv, "edge_count", 10, newSVuv(__atomic_load_n(&h->hdr->edge_count, __ATOMIC_ACQUIRE)), 0);
358 3           hv_store(hv, "max_nodes", 9, newSVuv(h->hdr->max_nodes), 0);
359 3           hv_store(hv, "max_edges", 9, newSVuv(h->hdr->max_edges), 0);
360 3           hv_store(hv, "ops", 3, newSVuv((UV)__atomic_load_n(&h->hdr->stat_ops, __ATOMIC_RELAXED)), 0);
361 3           hv_store(hv, "mmap_size", 9, newSVuv((UV)h->mmap_size), 0);
362 3           RETVAL = newRV_noinc((SV *)hv);
363             OUTPUT:
364             RETVAL
365              
366             SV *
367             path(self)
368             SV *self
369             PREINIT:
370 2 50         EXTRACT_GRAPH(self);
    50          
    50          
371             CODE:
372 2 100         RETVAL = h->path ? newSVpv(h->path, 0) : &PL_sv_undef;
373             OUTPUT:
374             RETVAL