summaryrefslogtreecommitdiff
path: root/cop.h
diff options
context:
space:
mode:
authorYves Orton <demerphq@gmail.com>2022-10-28 21:14:23 +0200
committerYves Orton <demerphq@gmail.com>2022-11-01 11:57:31 +0100
commit6760f691a95ab3a37fd59212795de2b1a7cf7888 (patch)
treecb90dfaef26bcfd6f84b048e20f6267ec3ded521 /cop.h
parente6421d31ff3b18d931e0da3152437c6f8d3f24a9 (diff)
downloadperl-6760f691a95ab3a37fd59212795de2b1a7cf7888.tar.gz
cop.h - add support for refcounted filenames in cops under threads
We have a weird bifurcation of the cop logic around threads. With threads we use a char * cop_file member, without it we use a GV * and replace cop_file with cop_filegv. The GV * code refcounts filenames and more or less efficiently shares the filename amongst many opcodes. However under threads we were simplify copying the filenames into each opcode. This is because in theory opcodes created in one thread can be destroyed in another. I say in theory because as far as I know the core code does not actually do this. But we have tests that you can construct a perl, clone it, and then destroy the original, and have the copy work just fine, this means that opcodes constructed in the main thread will be destroyed in the cloned thread. This in turn means that you can't put SV derived structures into the op-tree under threads. Which is why we can not use the GV * stategy under threads. As such this code adds a new struct/type RCPV, which is a refcounted string using shared memory. This is implemented in such a way that code that previously used a char * can continue to do so, as the refcounting data is located a specific offset before the char * pointer itself. This also allows the len data to embedded "into" the PV, which allows us to expose macros to acces the length of what is in theory a null terminated string. struct rcpv { UV refcount; STRLEN len; char pv[1]; }; typedef struct rcpv RCPV; The struct is sized appropriately on creation in rcpv_new() so that the pv member contains the full string plus a null byte. It then returns a pointer to the pv member of the struct. Thus the refcount and length and embedded at a predictable offset in front of the char *, which means we do not have to change any types for members using this. We provide three operations: rcpv_new(), rcpv_copy() and rcpv_free(), which roughly correspond with newSVpv(), SvREFCNT_inc(), SvREFCNT_dec(), and a handful of macros as well. We also expose SAVERCPVFREE which is similar to SAVEGENERICSV but operates on pv's constructed with rcpv_new(). Currently I have not restricted use of this logic to threaded perls. We simply do not use it in unthreaded perls, but I see no reason we couldn't normalize the code to use this in both cases, except possibly that actually the GV case is more efficient. Note that rcpv_new() does NOT use a hash table to dedup strings. Two calls to rcpv_new() with the same arguments will produce two distinct pointers with their own refcount data. Refcounting the cop_file data was Tony Cook's idea.
Diffstat (limited to 'cop.h')
-rw-r--r--cop.h93
1 files changed, 88 insertions, 5 deletions
diff --git a/cop.h b/cop.h
index f53f6aa7cb..a22825f954 100644
--- a/cop.h
+++ b/cop.h
@@ -439,7 +439,7 @@ struct cop {
#ifdef USE_ITHREADS
PADOFFSET cop_stashoff; /* offset into PL_stashpad, for the
package the line was compiled in */
- char * cop_file; /* name of file this command is from */
+ char * cop_file; /* rcpv containing name of file this command is from */
#else
HV * cop_stash; /* package line was compiled in */
GV * cop_filegv; /* name of GV file this command is from */
@@ -463,6 +463,9 @@ struct cop {
=for apidoc Am|const char *|CopFILE|const COP * c
Returns the name of the file associated with the C<COP> C<c>
+=for apidoc Am|const char *|CopFILE_LEN|const COP * c
+Returns the length of the file associated with the C<COP> C<c>
+
=for apidoc Am|line_t|CopLINE|const COP * c
Returns the line number in the source code associated with the C<COP> C<c>
@@ -479,6 +482,17 @@ Returns the SV associated with the C<COP> C<c>
=for apidoc Am|void|CopFILE_set|COP * c|const char * pv
Makes C<pv> the name of the file associated with the C<COP> C<c>
+=for apidoc Am|void|CopFILE_setn|COP * c|const char * pv|STRLEN len
+Makes C<pv> the name of the file associated with the C<COP> C<c>
+
+=for apidoc Am|void|CopFILE_copy|COP * dst|COP * src
+Efficiently copies the cop file name from one COP to another. Wraps
+the required logic to do a refcounted copy under threads or not.
+
+=for apidoc Am|void|CopFILE_free|COP * c
+Frees the file data in a cop. Under the hood this is a refcounting
+operation.
+
=for apidoc Am|GV *|CopFILEGV|const COP * c
Returns the GV associated with the C<COP> C<c>
@@ -506,14 +520,81 @@ string C<p>, creating the package if necessary.
=cut
*/
+/*
+=for apidoc Am|RCPV *|RCPVx|char *pv
+Returns the RCPV structure (struct rcpv) for a refcounted
+string pv created with C<rcpv_new()>.
+
+=for apidoc Am|RCPV *|RCPV_REFCOUNT|char *pv
+Returns the refcount for a pv created with C<rcpv_new()>.
+
+=for apidoc Am|RCPV *|RCPV_LEN|char *pv
+Returns the length of a pv created with C<rcpv_new()>.
+
+=cut
+*/
+
+struct rcpv {
+ STRLEN refcount; /* UV would mean a 64 refcnt on
+ 32 bit builds with -Duse64bitint */
+ STRLEN len;
+ char pv[1];
+};
+typedef struct rcpv RCPV;
+
+#define RCPVf_USE_STRLEN 1
+#define RCPVx(pv_arg) ((RCPV *)((pv_arg) - STRUCT_OFFSET(struct rcpv, pv)))
+#define RCPV_REFCOUNT(pv) (RCPVx(pv)->refcount)
+#define RCPV_LEN(pv) (RCPVx(pv)->len)
+
#ifdef USE_ITHREADS
-# define CopFILE(c) ((c)->cop_file)
+# define CopFILE(c) ((c)->cop_file)
+# define CopFILE_LEN(c) (CopFILE(c) ? RCPV_LEN(CopFILE(c)) : 0)
# define CopFILEGV(c) (CopFILE(c) \
? gv_fetchfile(CopFILE(c)) : NULL)
-# define CopFILE_set(c,pv) ((c)->cop_file = savesharedpv(pv))
-# define CopFILE_setn(c,pv,l) ((c)->cop_file = savesharedpvn((pv),(l)))
+# define CopFILE_set_x(c,pv) ((c)->cop_file = rcpv_new((pv),0,RCPVf_USE_STRLEN))
+# define CopFILE_setn_x(c,pv,l) ((c)->cop_file = rcpv_new((pv),(l),0))
+# define CopFILE_free_x(c) ((c)->cop_file = rcpv_free((c)->cop_file))
+# define CopFILE_copy_x(dst,src) ((dst)->cop_file = rcpv_copy((src)->cop_file))
+
+/* change condition to 1 && to enable this debugging */
+# define CopFILE_debug(c,t,rk) \
+ if (0 && (c)->cop_file) \
+ PerlIO_printf(Perl_debug_log, \
+ "%-14s THX:%p OP:%p PV:%p rc: " \
+ "%6zu fn: '%.*s' at %s line %d\n", \
+ (t), aTHX, (c), (c)->cop_file, \
+ RCPV_REFCOUNT((c)->cop_file)-rk, \
+ (int)RCPV_LEN((c)->cop_file), \
+ (c)->cop_file,__FILE__,__LINE__) \
+
+
+# define CopFILE_set(c,pv) \
+ STMT_START { \
+ CopFILE_set_x(c,pv); \
+ CopFILE_debug(c,"CopFILE_set", 0); \
+ } STMT_END
+
+# define CopFILE_setn(c,pv,l) \
+ STMT_START { \
+ CopFILE_setn_x(c,pv,l); \
+ CopFILE_debug(c,"CopFILE_setn", 0); \
+ } STMT_END
+
+# define CopFILE_copy(dst,src) \
+ STMT_START { \
+ CopFILE_copy_x((dst),(src)); \
+ CopFILE_debug((dst),"CopFILE_copy", 0); \
+ } STMT_END
+
+# define CopFILE_free(c) \
+ STMT_START { \
+ CopFILE_debug((c),"CopFILE_free", 1); \
+ CopFILE_free_x(c); \
+ } STMT_END
+
# define CopFILESV(c) (CopFILE(c) \
? GvSV(gv_fetchfile(CopFILE(c))) : NULL)
@@ -526,13 +607,13 @@ string C<p>, creating the package if necessary.
# define CopSTASH_set(c,hv) ((c)->cop_stashoff = (hv) \
? alloccopstash(hv) \
: 0)
-# define CopFILE_free(c) (PerlMemShared_free(CopFILE(c)),(CopFILE(c) = NULL))
#else /* Above: yes threads; Below no threads */
# define CopFILEGV(c) ((c)->cop_filegv)
# define CopFILEGV_set(c,gv) ((c)->cop_filegv = (GV*)SvREFCNT_inc(gv))
# define CopFILE_set(c,pv) CopFILEGV_set((c), gv_fetchfile(pv))
+# define CopFILE_copy(dst,src) CopFILEGV_set((dst),CopFILEGV(src))
# define CopFILE_setn(c,pv,l) CopFILEGV_set((c), gv_fetchfile_flags((pv),(l),0))
# define CopFILESV(c) (CopFILEGV(c) ? GvSV(CopFILEGV(c)) : NULL)
# define CopFILEAV(c) (CopFILEGV(c) ? GvAV(CopFILEGV(c)) : NULL)
@@ -544,6 +625,8 @@ string C<p>, creating the package if necessary.
# define CopFILEAVn(c) (CopFILEGV(c) ? GvAVn(CopFILEGV(c)) : NULL)
# define CopFILE(c) (CopFILEGV(c) /* +2 for '_<' */ \
? GvNAME(CopFILEGV(c))+2 : NULL)
+# define CopFILE_LEN(c) (CopFILEGV(c) /* -2 for '_<' */ \
+ ? GvNAMELEN(CopFILEGV(c))-2 : 0)
# define CopSTASH(c) ((c)->cop_stash)
# define CopSTASH_set(c,hv) ((c)->cop_stash = (hv))
# define CopFILE_free(c) (SvREFCNT_dec(CopFILEGV(c)),(CopFILEGV(c) = NULL))