Branch: Tag:

2001-06-26

2001-06-26 01:34:29 by Martin Stjernholm <mast@lysator.liu.se>

Fixed problem with thread safety in p-code evaluation.

Rev: server/etc/modules/RXML.pmod/module.pmod:1.174

2:   //   // Created 1999-07-30 by Martin Stjernholm.   // - // $Id: module.pmod,v 1.173 2001/06/25 19:20:45 mast Exp $ + // $Id: module.pmod,v 1.174 2001/06/26 01:34:29 mast Exp $      // Kludge: Must use "RXML.refs" somewhere for the whole module to be   // loaded correctly.
333:    mixed err = catch { \    if (!_frame->args) \    argfunc = _frame->_prepare (_ctx, _type, _args); \ -  _res = _frame->_eval (_ctx, _parser, _type, _content || ""); \ -  if (_parser->p_code) { \ -  _frame->args = argfunc; \ -  _parser->p_code->add (_frame); \ +  _res = _frame->_eval (_ctx, _parser, _type, 0, _content || ""); \ +  if (PCode p_code = _parser->p_code) { \ +  p_code->add (_frame); \ +  p_code->add (argfunc); \ +  p_code->add (_frame->content); \    } \    break eval_frame; \    }; \    \ -  if (_parser->p_code) { \ +  if (PCode p_code = _parser->p_code) { \    /* Make an unparsed frame in the p-code if the evaluation \    * fails. */ \    _frame->flags |= FLAG_UNPARSED; \    _frame->args = _args, _frame->content = _content || ""; \ -  _parser->p_code->add (_frame); \ +  werror ("err %O %O %O\n", _frame, _frame->args, _frame->content); \ +  p_code->add (_frame); \ +  p_code->add (0); \ +  p_code->add (0); \    } \    \    if (objectp (err) && ([object] err)->thrown_at_unwind) { \
1901:   //! be compiled in (normally enabled with the @tt{--debug@} flag to   //! Roxen).    - // Static flags (i.e. tested in the Tag object): + // Flags tested in the Tag object:      constant FLAG_PROC_INSTR = 0x00000010;   //! Flags this as a processing instruction tag (i.e. one parsed with
1932:   //! Postparse the result with the @[RXML.PXml] parser. This is only   //! used in the simple tag wrapper. Defined here as placeholder.    - // The rest of the flags are dynamic (i.e. tested in the Frame object): + // Flags tested in the Frame object:      constant FLAG_EMPTY_ELEMENT = 0x00000001;   //! If set, the tag does not use any content. E.g. with an HTML parser
2054:    //! Various bit flags that affect parsing. See the @tt{FLAG_*@}    //! constants. It's copied from @[Tag.flag] when the frame is    //! created. +  //! +  //! @note +  //! This variable may be set in the @tt{do_*@} callbacks, but it's +  //! assumed to be static, i.e. its value should not depend on any +  //! information that's known only at runtime. Practically that means +  //! that the value is assumed to never change if the frame is reused +  //! by p-code.    -  mapping(string:mixed)|EVAL_ARGS_FUNC args; +  mapping(string:mixed)|int(1..1) args;    //! The (parsed and evaluated) arguments passed to the tag. Set    //! every time the frame is executed, before any frame callbacks are    //! called. Not set for processing instruction (@[FLAG_PROC_INSTR])    //! tags.    //! -  //! This variable is also used to hold a function that generates the -  //! argument mapping between evaluations of the frame. It never does -  //! when any of the callbacks except @[cached_return] are called, -  //! though. +  //! This variable is also used to hold the value 1 which is used +  //! internally to signal that this frame is not currently evaluated. +  //! It's never 1 when any of the callbacks are executed.       Type content_type;    //! The type of the content.    -  mixed|PCode content = nil; +  mixed content;    //! The content, if any. Set before @[do_process] and @[do_return]    //! are called. Initialized to @[RXML.nil] every time the frame    //! executed. -  //! -  //! This variable is also used to hold an unevaluated representation -  //! of the content between evaluations of the frame. It never does -  //! when any of the callbacks except @[cached_return] are called, -  //! though. +        Type result_type;    //! The required result type. If it has a parser, it will affect how
2091:    //! necessary. An exception (which this frame can't catch) is thrown    //! if conversion is impossible.    -  mixed result = nil; +  mixed result;    //! The result, which is assumed to be either @[RXML.nil] or a valid    //! value according to result_type. The exec arrays returned by e.g.    //! @[do_return] changes this. It may also be set directly.
2118:    //! If set, the tags in this tag set will be used in addition to the    //! tags inherited from the surrounding parser. The additional tags    //! will in turn be inherited by subparsers. +  //! +  //! @note +  //! This variable may be set in the @[do_enter] callback, but it's +  //! assumed to be static, i.e. its value should not depend on any +  //! information that's known only at runtime. Practically that means +  //! that the value is assumed to never change if the frame is reused +  //! by p-code.       //! @decl optional TagSet local_tags;    //!    //! If set, the tags in this tag set will be used in the parser for    //! the content, instead of the one inherited from the surrounding    //! parser. The tags are not inherited by subparsers. -  +  //! +  //! @note +  //! This variable may be set in the @[do_enter] callback, but it's +  //! assumed to be static, i.e. its value should not depend on any +  //! information that's known only at runtime. Practically that means +  //! that the value is assumed to never change if the frame is reused +  //! by p-code.       //! @decl optional Frame parent_frame;    //!
2137:    //! If this variable exists, it gets the raw text representation of    //! the tag, if there is any. Note that it's after parsing of any    //! splice argument. +  //! +  //! @note +  //! This variable is assumed to be static, i.e. its value doesn't +  //! depend on any information that's known only at runtime.       //! @decl optional array do_enter (RequestID id);    //! @decl optional array do_process (RequestID id, void|mixed piece);
2261:    //! implemented.       optional array cached_return (Context ctx, void|mixed piece); -  //! If defined, this will be called to get the value from a cached -  //! frame (that's still valid) instead of using the cached result. -  //! It's otherwise handled like @[do_return]. Note that the cached -  //! frame may be used from several threads. FIXME: Not yet -  //! implemented. +  //! If defined, this will be called to get the value from the frame, +  //! instead of the normal @tt{do_*@} functions that evaluates the +  //! result from scratch. If it returns zero, the normal callbacks +  //! will be called as usual.    -  +  this_program clone() +  //! Called to create a copy of this frame, usually to perform +  //! evaluation in a thread safe way. Thus this function copies data +  //! that isn't the result of evaluation, like @[flags], +  //! @[content_type] and @[result_type] but not @[result] or @[vars]. +  //! +  //! A frame corresponds to a specific tag instance in a page, and +  //! clones of it also corresponds to the same tag instance. Since +  //! the frame is used to store data during the evaluation (i.e. +  //! between the @tt{do_*@} callbacks) of the frame it can be +  //! necessary to clone it to allow the same tag instance to be +  //! evaluated simultaneously in different threads. +  //! +  //! Tags should normally not override the default definition, but it +  //! can be useful together with @[cached_return] to share caches +  //! between frame clones. +  { +  this_program this = this_object(), clone = object_program (this)(); +  clone->tag = tag; +  clone->flags = flags; +  clone->args = args; +  clone->content_type = content_type; +  clone->content = content; +  clone->result_type = result_type; +  if (string v = this->scope_name) clone->scope_name = v; +  if (TagSet v = this->additional_tags) clone->additional_tags = v; +  if (TagSet v = this->local_tags) clone->local_tags = v; +  if (string v = this->raw_tag_text) clone->raw_tag_text = v; +  return clone; +  } +     // Services:       final mixed get_var (string var, void|string scope_name, void|Type want_type)
2780:       mixed err = catch {   #ifdef DEBUG -  if (!up) +  if (up) fatal_error ("up frame already set.\n");   #endif    up = ctx->frame;    ctx->frame = this; // Push the frame to get proper backtraces.
2970:    }    else THIS_TAG_DEBUG ("Keeping content_type %O\n", content_type);    -  ctx->frame = up; +  ctx->frame = up, up = 0;    ctx->frame_depth--;    return func;    };    -  ctx->frame = up; +  ctx->frame = up, up = 0;    ctx->frame_depth--;    throw (err);    }       mixed _eval (Context ctx, TagSetParser|PCode evaler, Type type, -  void|string|PCode in_content) +  void|EVAL_ARGS_FUNC in_args, void|string|PCode in_content)    // Note: It might be somewhat tricky to override this function,    // since it handles unwinding through exceptions.    {
2994:   #define EVSTAT_LAST_ITER 2   #define EVSTAT_ITER_DONE 3    int eval_state = EVSTAT_BEGIN; -  EVAL_ARGS_FUNC in_args; +  //in_args;    //in_content;    int iter;   #ifdef DEBUG
3013: Inside #if defined(DEBUG)
   PRE_INIT_ERROR ("Context not current.\n");    if (!evaler->tag_set_eval)    PRE_INIT_ERROR ("Calling _eval() with non-tag set parser.\n"); +  if (up) +  PRE_INIT_ERROR ("up frame already set.\n");    Frame prev_ctx_frame = ctx->frame;   #endif   #ifdef MODULE_DEBUG
3035:    if ((err1 = catch { // Catch errors but don't allow unwinds.    if (array state = ctx->unwind_state && ctx->unwind_state[this]) {   #ifdef DEBUG -  if (in_content) -  fatal_error ("Can't feed new content when resuming parse.\n"); +  if (in_args || in_content) +  fatal_error ("in_args or in_content set when resuming parse.\n");   #endif -  +  up = ctx->frame;    ctx->frame = this;    object ignored;    [ignored, eval_state, in_args, in_content, iter,
3080:    }    else if (in_content) {    THIS_TAG_TOP_DEBUG ("Evaluating\n"); +  up = ctx->frame;    ctx->frame = this; -  if (functionp (args)) { +  if (in_args) {    THIS_TAG_DEBUG ("Evaluating compiled arguments\n"); -  in_args = args; +     args = in_args (ctx);    }    content = nil;
3097:    piece = result = nil;    }    - #ifdef DEBUG -  if (up && up != prev_ctx_frame) -  fatal_error ("Frame probably mixed between different simultaneous contexts " -  "(up: %O, previous ctx->frame: %O).\n", up, prev_ctx_frame); - #endif -  +     if (TagSet add_tags = [object(TagSet)] this->additional_tags) {    TagSet tset = ctx->tag_set;    if (!tset->has_effective_tags (add_tags)) {
3455:       // Normal clean up on tag return or exception.    if (orig_tag_set) ctx->tag_set = orig_tag_set; -  ctx->frame = up; +  ctx->frame = up, up = 0;    ctx->frame_depth--;    if (err1) throw (err1); -  args = in_args, content = in_content; +  args = 1, content = in_content;    return conv_result;    }   
5701:    mixed item = p_code[pos];    if (objectp (item))    if (item->is_RXML_Frame) { -  item = item->_eval ( -  context, this_object(), type, item->content); // Might unwind. +  object frame = item; +  if (frame->args == 1) +  // Relying on the interpreter lock here. +  frame->args = 0; +  else +  frame = frame->clone(); +  item = frame->_eval ( // Might unwind. +  context, this_object(), type, p_code[++pos], p_code[++pos]);    }    else if (item->is_RXML_p_code_entry) {    item = item->get (context, type); // Might unwind.
5763:    mixed item = p_code[pos];    if (objectp (item))    if (item->is_RXML_Frame) { -  string itemvar = comp->bind (p_code[pos]); -  parts[pos] = sprintf ("%s->_eval (context, 0, %s, %s->content)", -  itemvar, typevar, itemvar); +  parts[pos] = sprintf ( +  "(%s->args == 1 ? %[0]s->args = 0, %[0]s : %[0]s->clone())" +  "->_eval (context, 0, %s, %s, %s)", +  comp->bind (p_code[pos]), +  typevar, +  comp->bind (p_code[++pos]), +  comp->bind (p_code[++pos]));    continue;    }    else if (item->is_RXML_VarRef) {