1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2  * contributor license agreements.  See the NOTICE file distributed with
3  * this work for additional information regarding copyright ownership.
4  * The ASF licenses this file to You under the Apache License, Version 2.0
5  * (the "License"); you may not use this file except in compliance with
6  * the License.  You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "mod_perl.h"
18 
19 /*
20  * modperl_svptr_table api is an add-on to the Perl ptr_table_ api.
21  * we use a PTR_TBL_t to map config structures (e.g. from parsed
22  * httpd.conf or .htaccess), where each interpreter needs to have its
23  * own copy of the Perl SV object.  we do not use an HV* for this, because
24  * the HV keys must be SVs with a string value, too much overhead.
25  * we do not use an apr_hash_t because they only have the lifetime of
26  * the pool used to create them. which may or may not be the same lifetime
27  * of the objects we need to lookup.
28  */
29 
30 #ifdef USE_ITHREADS
31 
32 #if MP_PERL_BRANCH(5, 6)
33 #   define my_sv_dup(s, p) SvREFCNT_inc(sv_dup(s))
34 
35 typedef struct {
36     AV *stashes;
37     UV flags;
38     PerlInterpreter *proto_perl;
39 } CLONE_PARAMS;
40 
41 #else
42 #   ifdef sv_dup_inc
43 #       define my_sv_dup(s, p) sv_dup_inc(s, p)
44 #   else
45 #       define my_sv_dup(s, p) SvREFCNT_inc(sv_dup(s, p))
46 #   endif
47 #endif
48 
49 /*
50  * copy a PTR_TBL_t whos PTR_TBL_ENT_t values are SVs.
51  * the SVs are dup-ed so each interpreter has its own copy.
52  */
modperl_svptr_table_clone(pTHX_ PerlInterpreter * proto_perl,PTR_TBL_t * source)53 PTR_TBL_t *modperl_svptr_table_clone(pTHX_ PerlInterpreter *proto_perl,
54                                      PTR_TBL_t *source)
55 {
56     UV i;
57     PTR_TBL_t *tbl;
58     PTR_TBL_ENT_t **src_ary, **dst_ary;
59     CLONE_PARAMS parms;
60 
61     Newxz(tbl, 1, PTR_TBL_t);
62     tbl->tbl_max        = source->tbl_max;
63     tbl->tbl_items        = source->tbl_items;
64     Newxz(tbl->tbl_ary, tbl->tbl_max + 1, PTR_TBL_ENT_t *);
65 
66     dst_ary = tbl->tbl_ary;
67     src_ary = source->tbl_ary;
68 
69     Zero(&parms, 1, CLONE_PARAMS);
70     parms.flags = 0;
71     parms.stashes = newAV();
72 
73     for (i=0; i < source->tbl_max; i++, dst_ary++, src_ary++) {
74         PTR_TBL_ENT_t *src_ent, *dst_ent=NULL;
75 
76         if (!*src_ary) {
77             continue;
78         }
79 
80         for (src_ent = *src_ary;
81              src_ent;
82              src_ent = src_ent->next)
83         {
84             if (dst_ent == NULL) {
85                 Newxz(dst_ent, 1, PTR_TBL_ENT_t);
86                 *dst_ary = dst_ent;
87             }
88             else {
89                 Newxz(dst_ent->next, 1, PTR_TBL_ENT_t);
90                 dst_ent = dst_ent->next;
91             }
92 
93             /* key is just a pointer we do not modify, no need to copy */
94             dst_ent->oldval = src_ent->oldval;
95 
96             dst_ent->newval = my_sv_dup((SV*)src_ent->newval, &parms);
97         }
98     }
99 
100     SvREFCNT_dec(parms.stashes);
101 
102     return tbl;
103 }
104 
105 #endif
106 
107 /*
108  * need to free the SV values in addition to ptr_table_free
109  */
modperl_svptr_table_destroy(pTHX_ PTR_TBL_t * tbl)110 void modperl_svptr_table_destroy(pTHX_ PTR_TBL_t *tbl)
111 {
112     UV i;
113     PTR_TBL_ENT_t **ary = tbl->tbl_ary;
114 
115     for (i=0; i < tbl->tbl_max; i++, ary++) {
116         PTR_TBL_ENT_t *ent;
117 
118         if (!*ary) {
119             continue;
120         }
121 
122         for (ent = *ary; ent; ent = ent->next) {
123             if (!ent->newval) {
124                 continue;
125             }
126 
127             SvREFCNT_dec((SV*)ent->newval);
128             ent->newval = NULL;
129         }
130     }
131 
132     modperl_svptr_table_free(aTHX_ tbl);
133 }
134 
135 /*
136  * the Perl ptr_table_ api does not provide a function to remove
137  * an entry from the table.  we need to SvREFCNT_dec the SV value
138  * anyhow.
139  */
modperl_svptr_table_delete(pTHX_ PTR_TBL_t * tbl,void * key)140 void modperl_svptr_table_delete(pTHX_ PTR_TBL_t *tbl, void *key)
141 {
142     PTR_TBL_ENT_t *entry, **oentry;
143     UV hash = PTR2UV(key);
144 
145     oentry = &tbl->tbl_ary[hash & tbl->tbl_max];
146     entry = *oentry;
147 
148     for (; entry; oentry = &entry->next, entry = *oentry) {
149         if (entry->oldval == key) {
150             *oentry = entry->next;
151             SvREFCNT_dec((SV*)entry->newval);
152             Safefree(entry);
153             tbl->tbl_items--;
154             return;
155         }
156     }
157 }
158 
159 /*
160  * XXX: the following are a copy of the Perl 5.8.0 Perl_ptr_table api
161  * renamed s/Perl_ptr/modperl_svptr/g;
162  * two reasons:
163  *   these functions do not exist without -DUSE_ITHREADS
164  *   the clear/free functions do not exist in 5.6.x
165  */
166 
167 /* create a new pointer-mapping table */
168 
169 PTR_TBL_t *
modperl_svptr_table_new(pTHX)170 modperl_svptr_table_new(pTHX)
171 {
172     PTR_TBL_t *tbl;
173     Newxz(tbl, 1, PTR_TBL_t);
174     tbl->tbl_max        = 511;
175     tbl->tbl_items        = 0;
176     Newxz(tbl->tbl_ary, tbl->tbl_max + 1, PTR_TBL_ENT_t*);
177     return tbl;
178 }
179 
180 /* map an existing pointer using a table */
181 
182 void *
modperl_svptr_table_fetch(pTHX_ PTR_TBL_t * tbl,void * sv)183 modperl_svptr_table_fetch(pTHX_ PTR_TBL_t *tbl, void *sv)
184 {
185     PTR_TBL_ENT_t *tblent;
186     UV hash = PTR2UV(sv);
187     MP_ASSERT(tbl);
188     tblent = tbl->tbl_ary[hash & tbl->tbl_max];
189     for (; tblent; tblent = tblent->next) {
190         if (tblent->oldval == sv)
191             return tblent->newval;
192     }
193     return (void*)NULL;
194 }
195 
196 /* add a new entry to a pointer-mapping table */
197 
198 void
modperl_svptr_table_store(pTHX_ PTR_TBL_t * tbl,void * oldv,void * newv)199 modperl_svptr_table_store(pTHX_ PTR_TBL_t *tbl, void *oldv, void *newv)
200 {
201     PTR_TBL_ENT_t *tblent, **otblent;
202     /* XXX this may be pessimal on platforms where pointers aren't good
203      * hash values e.g. if they grow faster in the most significant
204      * bits */
205     UV hash = PTR2UV(oldv);
206     bool i = 1;
207 
208     MP_ASSERT(tbl);
209     otblent = &tbl->tbl_ary[hash & tbl->tbl_max];
210     for (tblent = *otblent; tblent; i=0, tblent = tblent->next) {
211         if (tblent->oldval == oldv) {
212             tblent->newval = newv;
213             return;
214         }
215     }
216     Newxz(tblent, 1, PTR_TBL_ENT_t);
217     tblent->oldval = oldv;
218     tblent->newval = newv;
219     tblent->next = *otblent;
220     *otblent = tblent;
221     tbl->tbl_items++;
222     if (i && tbl->tbl_items > tbl->tbl_max)
223         modperl_svptr_table_split(aTHX_ tbl);
224 }
225 
226 /* double the hash bucket size of an existing ptr table */
227 
228 void
modperl_svptr_table_split(pTHX_ PTR_TBL_t * tbl)229 modperl_svptr_table_split(pTHX_ PTR_TBL_t *tbl)
230 {
231     PTR_TBL_ENT_t **ary = tbl->tbl_ary;
232     UV oldsize = tbl->tbl_max + 1;
233     UV newsize = oldsize * 2;
234     UV i;
235 
236     Renew(ary, newsize, PTR_TBL_ENT_t*);
237     Zero(&ary[oldsize], newsize-oldsize, PTR_TBL_ENT_t*);
238     tbl->tbl_max = --newsize;
239     tbl->tbl_ary = ary;
240     for (i=0; i < oldsize; i++, ary++) {
241         PTR_TBL_ENT_t **curentp, **entp, *ent;
242         if (!*ary)
243             continue;
244         curentp = ary + oldsize;
245         for (entp = ary, ent = *ary; ent; ent = *entp) {
246             if ((newsize & PTR2UV(ent->oldval)) != i) {
247                 *entp = ent->next;
248                 ent->next = *curentp;
249                 *curentp = ent;
250                 continue;
251             }
252             else
253                 entp = &ent->next;
254         }
255     }
256 }
257 
258 /* remove all the entries from a ptr table */
259 
260 void
modperl_svptr_table_clear(pTHX_ PTR_TBL_t * tbl)261 modperl_svptr_table_clear(pTHX_ PTR_TBL_t *tbl)
262 {
263     register PTR_TBL_ENT_t **array;
264     register PTR_TBL_ENT_t *entry;
265     register PTR_TBL_ENT_t *oentry = (PTR_TBL_ENT_t *)NULL;
266     UV riter = 0;
267     UV max;
268 
269     if (!tbl || !tbl->tbl_items) {
270         return;
271     }
272 
273     array = tbl->tbl_ary;
274     entry = array[0];
275     max = tbl->tbl_max;
276 
277     for (;;) {
278         if (entry) {
279             oentry = entry;
280             entry = entry->next;
281             Safefree(oentry);
282         }
283         if (!entry) {
284             if (++riter > max) {
285                 break;
286             }
287             entry = array[riter];
288         }
289     }
290 
291     tbl->tbl_items = 0;
292 }
293 
294 /* clear and free a ptr table */
295 
296 void
modperl_svptr_table_free(pTHX_ PTR_TBL_t * tbl)297 modperl_svptr_table_free(pTHX_ PTR_TBL_t *tbl)
298 {
299     if (!tbl) {
300         return;
301     }
302     modperl_svptr_table_clear(aTHX_ tbl);
303     Safefree(tbl->tbl_ary);
304     Safefree(tbl);
305 }
306 
307 /*
308  * Local Variables:
309  * c-basic-offset: 4
310  * indent-tabs-mode: nil
311  * End:
312  */
313