File Coverage

Shared.xs
Criterion Covered Total %
statement 113 121 93.3
branch 131 252 51.9
condition n/a
subroutine n/a
pod n/a
total 244 373 65.4


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