File Coverage

Shared.xs
Criterion Covered Total %
statement 106 106 100.0
branch 94 144 65.2
condition n/a
subroutine n/a
pod n/a
total 200 250 80.0


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 "dsu.h"
7              
8             #define EXTRACT(sv) \
9             if (!sv_isobject(sv) || !sv_derived_from(sv, "Data::DisjointSet::Shared")) \
10             croak("Expected a Data::DisjointSet::Shared object"); \
11             DsuHandle *h = INT2PTR(DsuHandle*, SvIV(SvRV(sv))); \
12             if (!h) croak("Attempted to use a destroyed Data::DisjointSet::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             MODULE = Data::DisjointSet::Shared PACKAGE = Data::DisjointSet::Shared
21              
22             PROTOTYPES: DISABLE
23              
24             SV *
25             new(class, path = &PL_sv_undef, n = 0)
26             const char *class
27             SV *path
28             UV n
29             PREINIT:
30             char errbuf[DSU_ERR_BUFLEN];
31             CODE:
32 19 100         const char *p = SvOK(path) ? SvPV_nolen(path) : NULL;
33 19           DsuHandle *h = dsu_create(p, (uint64_t)n, errbuf); /* validates args into errbuf */
34 19 100         if (!h) croak("Data::DisjointSet::Shared->new: %s", errbuf);
35 17           MAKE_OBJ(class, h);
36             OUTPUT:
37             RETVAL
38              
39             SV *
40             new_memfd(class, name = &PL_sv_undef, n = 0)
41             const char *class
42             SV *name
43             UV n
44             PREINIT:
45             char errbuf[DSU_ERR_BUFLEN];
46             CODE:
47 3 100         const char *nm = SvOK(name) ? SvPV_nolen(name) : NULL; /* undef -> default label */
48 3           DsuHandle *h = dsu_create_memfd(nm, (uint64_t)n, errbuf); /* validates args into errbuf */
49 3 100         if (!h) croak("Data::DisjointSet::Shared->new_memfd: %s", errbuf);
50 2           MAKE_OBJ(class, h);
51             OUTPUT:
52             RETVAL
53              
54             SV *
55             new_from_fd(class, fd)
56             const char *class
57             int fd
58             PREINIT:
59             char errbuf[DSU_ERR_BUFLEN];
60             CODE:
61 2           DsuHandle *h = dsu_open_fd(fd, errbuf);
62 2 100         if (!h) croak("Data::DisjointSet::Shared->new_from_fd: %s", errbuf);
63 1           MAKE_OBJ(class, h);
64             OUTPUT:
65             RETVAL
66              
67             void
68             DESTROY(self)
69             SV *self
70             CODE:
71 22 50         if (sv_isobject(self) && sv_derived_from(self, "Data::DisjointSet::Shared")) {
    50          
72 22           DsuHandle *h = INT2PTR(DsuHandle*, SvIV(SvRV(self)));
73 22 100         if (h) { sv_setiv(SvRV(self), 0); dsu_destroy(h); } /* null first: activates EXTRACT's use-after-destroy croak + makes a double DESTROY a no-op */
74             }
75              
76             UV
77             find(self, x)
78             SV *self
79             UV x
80             PREINIT:
81 3041 50         EXTRACT(self);
    50          
    100          
82             CODE:
83             /* Range-check BEFORE locking so a croak holds no lock. */
84 3040 100         if (x >= h->hdr->n)
85 1           croak("Data::DisjointSet::Shared->find: index %" UVuf " out of range (n=%u)",
86             x, h->hdr->n);
87             /* find performs path compression -> it MUTATES -> take the write lock. */
88 3039           dsu_rwlock_wrlock(h);
89 3039           RETVAL = (UV)dsu_find(h, (uint32_t)x);
90 3039           __atomic_fetch_add(&h->hdr->stat_ops, 1, __ATOMIC_RELAXED);
91 3039           dsu_rwlock_wrunlock(h);
92             OUTPUT:
93             RETVAL
94              
95             IV
96             union(self, a, b)
97             SV *self
98             UV a
99             UV b
100             PREINIT:
101 2014 50         EXTRACT(self);
    50          
    50          
102             CODE:
103 2014 100         if (a >= h->hdr->n)
104 1           croak("Data::DisjointSet::Shared->union: index %" UVuf " out of range (n=%u)",
105             a, h->hdr->n);
106 2013 100         if (b >= h->hdr->n)
107 1           croak("Data::DisjointSet::Shared->union: index %" UVuf " out of range (n=%u)",
108             b, h->hdr->n);
109 2012           dsu_rwlock_wrlock(h);
110 2012           RETVAL = (IV)dsu_union_locked(h, (uint32_t)a, (uint32_t)b); /* 1 = newly merged, 0 = already together */
111 2012           __atomic_fetch_add(&h->hdr->stat_ops, 1, __ATOMIC_RELAXED);
112 2012           dsu_rwlock_wrunlock(h);
113             OUTPUT:
114             RETVAL
115              
116             bool
117             connected(self, a, b)
118             SV *self
119             UV a
120             UV b
121             PREINIT:
122 5654 50         EXTRACT(self);
    50          
    50          
123             CODE:
124 5654 100         if (a >= h->hdr->n)
125 1           croak("Data::DisjointSet::Shared->connected: index %" UVuf " out of range (n=%u)",
126             a, h->hdr->n);
127 5653 100         if (b >= h->hdr->n)
128 1           croak("Data::DisjointSet::Shared->connected: index %" UVuf " out of range (n=%u)",
129             b, h->hdr->n);
130             /* connected compresses paths via dsu_find -> it MUTATES -> write lock. */
131 5652           dsu_rwlock_wrlock(h);
132 5652           RETVAL = dsu_connected_locked(h, (uint32_t)a, (uint32_t)b);
133 5652           __atomic_fetch_add(&h->hdr->stat_ops, 1, __ATOMIC_RELAXED);
134 5652           dsu_rwlock_wrunlock(h);
135             OUTPUT:
136             RETVAL
137              
138             UV
139             union_many(self, pairs)
140             SV *self
141             SV *pairs
142             PREINIT:
143 10 50         EXTRACT(self);
    50          
    50          
144             AV *av;
145             IV top;
146             CODE:
147 10 100         if (!SvROK(pairs) || SvTYPE(SvRV(pairs)) != SVt_PVAV)
    50          
148 1           croak("Data::DisjointSet::Shared->union_many: expected an array reference");
149 9           av = (AV *)SvRV(pairs);
150 9           top = av_len(av); /* last index, -1 if empty */
151             {
152 9 100         STRLEN cnt = (top >= 0) ? (STRLEN)(top + 1) : 0, i;
153             STRLEN npairs;
154 9           uint32_t *vals = NULL;
155 9           UV merged = 0;
156 9           uint32_t n = h->hdr->n;
157 9 100         if (cnt & 1)
158 1           croak("Data::DisjointSet::Shared->union_many: expected an even number of elements (flat [a0,b0,a1,b1,...]), got %" UVuf,
159             (UV)cnt);
160 8           npairs = cnt / 2;
161 8 100         if (cnt) { /* resolve + range-check ALL before locking */
162 7 50         Newx(vals, cnt, uint32_t); SAVEFREEPV(vals); /* freed on return OR unwind */
163 41 100         for (i = 0; i < cnt; i++) { /* a croak here holds NO lock; SAVEFREEPV cleans up */
164 36           SV **el = av_fetch(av, (SSize_t)i, 0);
165 36 50         UV v = (el && *el) ? SvUV(*el) : 0;
    50          
166 36 100         if (v >= n)
167 2           croak("Data::DisjointSet::Shared->union_many: index %" UVuf " out of range (n=%u)",
168             v, n);
169 34           vals[i] = (uint32_t)v;
170             }
171             }
172 6           dsu_rwlock_wrlock(h); /* locked region: NO croak-capable calls */
173 20 100         for (i = 0; i < npairs; i++)
174 14           merged += (UV)dsu_union_locked(h, vals[2*i], vals[2*i + 1]);
175 6           __atomic_fetch_add(&h->hdr->stat_ops, 1, __ATOMIC_RELAXED); /* a call always counts, even an empty batch */
176 6           dsu_rwlock_wrunlock(h);
177 6 50         RETVAL = merged;
178             }
179             OUTPUT:
180             RETVAL
181              
182             UV
183             set_size(self, x)
184             SV *self
185             UV x
186             PREINIT:
187 2130 50         EXTRACT(self);
    50          
    50          
188             CODE:
189 2130 100         if (x >= h->hdr->n)
190 1           croak("Data::DisjointSet::Shared->set_size: index %" UVuf " out of range (n=%u)",
191             x, h->hdr->n);
192             /* set_size compresses paths via dsu_find -> it MUTATES -> write lock. */
193 2129           dsu_rwlock_wrlock(h);
194 2129           RETVAL = (UV)dsu_set_size_locked(h, (uint32_t)x);
195 2129           __atomic_fetch_add(&h->hdr->stat_ops, 1, __ATOMIC_RELAXED);
196 2129           dsu_rwlock_wrunlock(h);
197             OUTPUT:
198             RETVAL
199              
200             UV
201             num_sets(self)
202             SV *self
203             PREINIT:
204 2016 50         EXTRACT(self);
    50          
    50          
205             CODE:
206 2016           dsu_rwlock_rdlock(h);
207 2016           RETVAL = (UV)h->hdr->num_sets;
208 2016           dsu_rwlock_rdunlock(h);
209             OUTPUT:
210             RETVAL
211              
212             UV
213             capacity(self)
214             SV *self
215             PREINIT:
216 4 50         EXTRACT(self);
    50          
    50          
217             CODE:
218 4 50         RETVAL = (UV)h->hdr->n; /* immutable after creation -- lock-free */
219             OUTPUT:
220             RETVAL
221              
222             void
223             reset(self)
224             SV *self
225             PREINIT:
226 2 50         EXTRACT(self);
    50          
    50          
227             CODE:
228 2           dsu_rwlock_wrlock(h);
229 2           dsu_reset_locked(h);
230 2           __atomic_fetch_add(&h->hdr->stat_ops, 1, __ATOMIC_RELAXED);
231 2           dsu_rwlock_wrunlock(h);
232              
233             SV *
234             stats(self)
235             SV *self
236             PREINIT:
237 1 50         EXTRACT(self);
    50          
    50          
238             CODE:
239             {
240             uint32_t n, num_sets;
241             uint64_t ops;
242             /* Snapshot under the lock; do all (croak-capable) Perl allocation after
243             releasing it -- so an OOM in newHV/newSV* can never strand the lock. */
244 1           dsu_rwlock_rdlock(h);
245 1           n = h->hdr->n;
246 1           num_sets = h->hdr->num_sets;
247 1           ops = h->hdr->stat_ops;
248 1           dsu_rwlock_rdunlock(h);
249              
250 1           HV *hv = newHV();
251 1           hv_stores(hv, "capacity", newSVuv((UV)n));
252 1           hv_stores(hv, "sets", newSVuv((UV)num_sets));
253 1           hv_stores(hv, "ops", newSVuv((UV)ops));
254 1           hv_stores(hv, "mmap_size", newSVuv((UV)h->mmap_size));
255 1           RETVAL = newRV_noinc((SV *)hv);
256             }
257             OUTPUT:
258             RETVAL
259              
260             SV *
261             path(self)
262             SV *self
263             PREINIT:
264 3 50         EXTRACT(self);
    50          
    50          
265             CODE:
266 3 100         RETVAL = h->path ? newSVpv(h->path, 0) : &PL_sv_undef;
267             OUTPUT:
268             RETVAL
269              
270             int
271             memfd(self)
272             SV *self
273             PREINIT:
274 3 50         EXTRACT(self);
    50          
    50          
275             CODE:
276 3 50         RETVAL = h->backing_fd;
277             OUTPUT:
278             RETVAL
279              
280             void
281             sync(self)
282             SV *self
283             PREINIT:
284 2 50         EXTRACT(self);
    50          
    50          
285             CODE:
286 2 50         if (dsu_msync(h) != 0) croak("sync: %s", strerror(errno));
287              
288             void
289             unlink(self, ...)
290             SV *self
291             CODE:
292 3 100         if (sv_isobject(self) && sv_derived_from(self, "Data::DisjointSet::Shared")) {
    50          
293 1           DsuHandle *h = INT2PTR(DsuHandle*, SvIV(SvRV(self)));
294 1 50         if (h && h->path) unlink(h->path);
    50          
295 1 50         } else if (items >= 2 && SvOK(ST(1))) {
    50          
296 1           unlink(SvPV_nolen(ST(1)));
297             }