Branch: Tag:

2002-04-17

2002-04-17 00:44:04 by Martin Stjernholm <mast@lysator.liu.se>

Handle the PikeCompile objects properly when delayed resolving p-code in
PCode._eval. This fixes the problem which could give the backtrace 'Cannot
index the NULL value with "resolve"' in situations with concurrent requests.

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

2:   //   // Created 1999-07-30 by Martin Stjernholm.   // - // $Id: module.pmod,v 1.280 2002/04/09 10:53:06 mast Exp $ + // $Id: module.pmod,v 1.281 2002/04/17 00:44:04 mast Exp $      // Kludge: Must use "RXML.refs" somewhere for the whole module to be   // loaded correctly.
2094:    make_p_code = 1;    Parser parser = type->get_parser (    this_object(), tag_set_override, 0, -  stale_safe ? RenewablePCode (type, tag_set) : PCode (type, tag_set)); +  stale_safe ? +  RenewablePCode (type, this_object(), tag_set) : +  PCode (type, this_object(), tag_set));       mixed res;    PCode p_code;
2236:    // changed. Never negative.       PikeCompile p_code_comp; -  // The PikeCompile object for collecting any produce Pike byte code +  // The PikeCompile object for collecting any produced Pike byte code    // during p-code compilation.       static void create (void|TagSet _tag_set, void|RequestID _id)
3296:    PCode p_code = 0;    if (TagSet local_tags = this_object()->local_tags) {    if ((ctx->make_p_code = flags & FLAG_COMPILE_RESULT)) { -  p_code = RenewablePCode (result_type, local_tags); +  p_code = RenewablePCode (result_type, ctx, local_tags);    p_code->source = [string] elem;    }    subparser = result_type->get_parser (ctx, local_tags, evaler, p_code);
3308:    }    else {    if ((ctx->make_p_code = flags & FLAG_COMPILE_RESULT)) { -  p_code = RenewablePCode (result_type, ctx->tag_set); +  p_code = RenewablePCode (result_type, ctx, ctx->tag_set);    p_code->source = [string] elem;    }    subparser = result_type->get_parser (
3574:    if (make_p_code) {    fn_text_add = (fn_text = String.Buffer())->add;    fn_text_add ("mixed tmp;\n"); -  sub_p_code = PCode (0); +  sub_p_code = PCode (0, 0);    comp = ctx->p_code_comp;    }       if (splice_arg) {    // Note: This assumes an XML-like parser. -  if (make_p_code) sub_p_code->create (splice_arg_type, ctx->tag_set); +  if (make_p_code) sub_p_code->create (splice_arg_type, ctx, ctx->tag_set);    Parser parser = splice_arg_type->get_parser (ctx, ctx->tag_set, 0,    sub_p_code);    THIS_TAG_DEBUG ("Evaluating splice argument %s\n",
3631:    foreach (indices (raw_args), string arg) {    Type t = atypes[arg] || default_type;    if (t->parser_prog != PNone) { -  sub_p_code->create (t, ctx_tag_set); +  sub_p_code->create (t, ctx, ctx_tag_set);    Parser parser = t->get_parser (ctx, ctx_tag_set, 0, sub_p_code);    THIS_TAG_DEBUG ("Evaluating and compiling "    "argument value %s with %O\n",
3976:    finished = 0; // Continuing an unwound subevaler.    else if (!in_content || in_content == "") {    if (flags & FLAG_GET_EVALED_CONTENT) { -  this_object()->evaled_content = PCode (content_type); +  this_object()->evaled_content = PCode (content_type, ctx);    this_object()->evaled_content->finish();    }    break eval_content; // No content to handle.
3993:    if (TagSet local_tags =    [object(TagSet)] this_object()->local_tags) {    PCode p_code = unevaled_content = -  ctx->make_p_code && PCode (content_type, local_tags); +  ctx->make_p_code && PCode (content_type, ctx, local_tags);    if (flags & FLAG_GET_EVALED_CONTENT) {    PCode more_p_code = this_object()->evaled_content = -  PCode (content_type, local_tags, ctx); +  PCode (content_type, ctx, local_tags, 1);    if (p_code) p_code->p_code = more_p_code;    else p_code = more_p_code;    }
4012:    }    else {    PCode p_code = unevaled_content = -  ctx->make_p_code && PCode (content_type, ctx->tag_set); +  ctx->make_p_code && PCode (content_type, ctx, ctx->tag_set);    if (flags & FLAG_GET_EVALED_CONTENT) {    PCode more_p_code = this_object()->evaled_content = -  PCode (content_type, ctx->tag_set, ctx); +  PCode (content_type, ctx, ctx->tag_set, 1);    if (p_code) p_code->p_code = more_p_code;    else p_code = more_p_code;    }
4046:    subevaler = in_content;    if (flags & FLAG_GET_EVALED_CONTENT) {    PCode p_code = this_object()->evaled_content = -  PCode (content_type, subevaler->tag_set, ctx); +  PCode (content_type, ctx, subevaler->tag_set, 1);    if (subevaler->recover_errors)    p_code->recover_errors = 1;    }
5328:    //! directly, otherwise a new object is created.    {    PCode p_code = 0; -  if (make_p_code) { -  p_code = objectp (make_p_code) ? make_p_code : PCode (this_object(), tag_set); -  if (!ctx->p_code_comp) ctx->p_code_comp = PikeCompile(); -  } +  if (make_p_code) +  p_code = objectp (make_p_code) ? make_p_code : PCode (this_object(), ctx, tag_set);       Parser p;    if (_p_cache) { // It's a tag set parser.
6697:   #endif      static class PikeCompile - //! Helper class to paste together a Pike program from strings. + //! Helper class to paste together a Pike program from strings. This + //! is thread safe.   {    static int idnr = 0;    static inherit String.Buffer: code; -  +  static inherit Thread.Mutex: mutex;    static mapping(string:mixed) bindings = ([]);    static mapping(string:int) cur_ids = ([]);    static mapping(mixed:mixed) delayed_resolve_places = ([]);
6716:    string add_var (string type, void|string init)    {    string id = "v" + idnr++; +  string txt; +     if (init) {    COMP_MSG ("%O add var: %s %s = %O\n", this_object(), type, id, init); -  code::add (sprintf ("%s %s = %s;\n", type, id, init)); +  txt = sprintf ("%s %s = %s;\n", type, id, init);    }    else {    COMP_MSG ("%O add var: %s %s\n", this_object(), type, id); -  code::add (sprintf ("%s %s;\n", type, id)); +  txt = sprintf ("%s %s;\n", type, id);    } -  +  +  code::add (txt); +  // Relying on the interpreter lock here.    cur_ids[id] = 1; -  +     return id;    }   
6733:    string id = "f" + idnr++;    COMP_MSG ("%O add func: %s %s (%s)\n{%s}\n",    this_object(), rettype, id, arglist, def); -  code::add (sprintf ("%s %s (%s)\n{%s}\n", rettype, id, arglist, def)); +  string txt = sprintf ("%s %s (%s)\n{%s}\n", rettype, id, arglist, def); +  +  code::add (txt); +  // Relying on the interpreter lock here.    cur_ids[id] = 1; -  +     return id;    }   
6758:   #endif    mixed resolved;    if (zero_type (resolved = bindings[what[index]])) { - #ifdef DEBUG -  if (!cur_ids[what[index]]) -  error ("Unknown binding %O.\n", what[index]); - #endif -  COMP_MSG ("%O delayed_resolve %O\n", this_object(), what[index]); +     delayed_resolve_places[what] = index; -  +  COMP_MSG ("%O delayed_resolve %O\n", this_object(), what[index]);    }    else { -  COMP_MSG ("%O delayed_resolve immediately %O\n", this_object(), what[index]); +     what[index] = resolved; -  +  COMP_MSG ("%O delayed_resolve immediately %O\n", this_object(), what[index]);    }    }   
6791:       object compile()    { +  Thread.MutexKey lock = mutex::lock();    object compiled = 0;    -  if (code::_sizeof()) { +  // vvv Relying on the interpreter lock from here. +  string txt = code::get(); +  mapping(string:int) loc_cur_ids = cur_ids; +  cur_ids = ([]); +  // ^^^ Relying on the interpreter lock to here. +  +  if (txt != "") {    COMP_MSG ("%O compile\n", this_object()); -  code::add("mixed _encode() { } void _decode(mixed v) { }\n" +  +  txt += +  "mixed _encode() { } void _decode(mixed v) { }\n"    "constant is_RXML_pike_code = 1;\n"    "constant is_RXML_encodable = 1;\n"   #ifdef RXML_OBJ_DEBUG -  // Don't want to encode the cloning of -  // RoxenDebug.ObjectMarker in the __INIT that is -  // dumped, since that debug might not be wanted when -  // the dump is decoded. +  // Don't want to encode the cloning of RoxenDebug.ObjectMarker +  // in the __INIT that is dumped, since that debug might not be +  // wanted when the dump is decoded.    "mapping|object __object_marker = ",    bind (RoxenDebug.ObjectMarker ("object(compiled RXML code)")), ";\n"   #else
6813: Inside #if defined(DEBUG)
   LITERAL (OBJ_COUNT)    ";}\n"   #endif -  ); +  ;       program res; -  string txt = code::get(); +    #ifdef DEBUG    if (mixed err = catch {   #endif
6830:       compiled = res();    -  foreach (indices (cur_ids), string i) +  foreach (indices (loc_cur_ids), string i)    bindings[i] = compiled[i]; -  cur_ids = ([]); +     }       foreach (indices (delayed_resolve_places), mixed what) {    mixed index = m_delete (delayed_resolve_places, what); - #ifdef DEBUG +     if (zero_type (bindings[what[index]])) -  error ("Unknown delayed id %O.\n", what[index]); - #endif -  COMP_MSG ("%O resolved delayed %O\n", this_object(), what[index]); +  delayed_resolve_places[what] = index; +  else {    what[index] = bindings[what[index]]; -  +  COMP_MSG ("%O resolved delayed %O\n", this_object(), what[index]);    } -  +  }       return compiled;    }
6851:    static void destroy()    {    compile(); // To clean up delayed_resolve_places. + #ifdef DEBUG +  if (sizeof (delayed_resolve_places)) { +  string errmsg = "Still got unresolved delayed resolve places:\n"; +  foreach (indices (delayed_resolve_places), mixed what) { +  mixed index = m_delete (delayed_resolve_places, what); +  errmsg += replace (sprintf (" %O[%O]: %O", what, index, what[index]), +  "\n", "\n ") + "\n";    } -  +  error (errmsg); +  } + #endif +  }       //! @ignore    MARK_OBJECT;
6984:    return piece;    }    -  void create (Type _type, void|TagSet _tag_set, void|Context collect_results) +  void create (Type _type, Context ctx, void|TagSet _tag_set, void|int collect_results) +  // Not static since this is also used to reset p-code objects.    {    if (collect_results) {    // Yes, the internal interaction between create, reset, the    // context and CTX_ALREADY_GOT_VC is ugly.    flags = COLLECT_RESULTS; -  if (collect_results->misc->variable_changes) flags |= CTX_ALREADY_GOT_VC; -  collect_results->misc->variable_changes = ([]); +  if (ctx->misc->variable_changes) flags |= CTX_ALREADY_GOT_VC; +  ctx->misc->variable_changes = ([]);    }    else flags = 0;    if (_type) {
7004:    length = 0;    flags |= UPDATED;    protocol_cache_time = -1; +  p_code_comp = ctx->p_code_comp || (ctx->p_code_comp = PikeCompile());    if (flags & COLLECT_RESULTS)    PCODE_MSG ("create or reset for result collection\n");    else
7020:    /*static*/ int length;       /*static*/ int flags; -  static constant FULLY_RESOLVED = 0x1; +     static constant COLLECT_RESULTS = 0x2;    static constant CTX_ALREADY_GOT_VC = 0x4; // Just as ugly as it sounds, but who cares?    static constant UPDATED = 0x8;
7038:    // is finished. It's reinstated on entry whenever the p-code is used    // to ensure that the protocol cache doesn't overcache.    +  static PikeCompile p_code_comp; +     void add (Context ctx, mixed entry, mixed evaled_value)    {   #ifdef DEBUG
7138:    else {    frame_state = exec[length + 2] = frame->_save();    if (stringp (frame_state[0])) -  ctx->p_code_comp->delayed_resolve (frame_state, 0); +  p_code_comp->delayed_resolve (frame_state, 0);    RESET_FRAME (frame);    }   
7348:    parts[ppos++] = errmsgs; \    } while (0)    -  if (flags & FULLY_RESOLVED) -  EVAL_LOOP (;, ;); -  else +  if (p_code_comp)    EVAL_LOOP ({    array frame_state = exec[pos + 2];    if (stringp (frame->args))    if (stringp (frame_state[0]))    frame->args = frame_state[0] = -  ctx->p_code_comp->resolve (frame_state[0]); +  p_code_comp->resolve (frame_state[0]);    else    frame->args = frame_state[0];    }, {    array frame_state = exec[pos + 2];    if (stringp (frame_state[0])) -  frame_state[0] = ctx->p_code_comp->resolve (frame_state[0]); +  frame_state[0] = p_code_comp->resolve (frame_state[0]);    }); -  +  else +  EVAL_LOOP (;, ;);       ctx->eval_finish();    if (ctx->state_updated > update_count) flags |= UPDATED;
7551:    if (exec[pos + 1]) exec[pos + 1]->args = encode_p_code[pos + 2][0];    }    } -  flags |= FULLY_RESOLVED; +  p_code_comp = 0;    -  return ({P_CODE_VERSION, flags & (COLLECT_RESULTS|FULLY_RESOLVED|FINISHED), +  return ({P_CODE_VERSION, flags & (COLLECT_RESULTS|FINISHED),    tag_set, tag_set && tag_set->get_hash(),    type, recover_errors, encode_p_code, protocol_cache_time});    }
7616:    if (mixed err = catch {    ctx->make_p_code = 1;    if (!parser) { -  renewed_p_code = PCode (type, tag_set); +  renewed_p_code = PCode (type, ctx, tag_set);    renewed_p_code->recover_errors = recover_errors;    renewed_p_code->p_code = new_p_code;    parser = type->get_parser (ctx, tag_set, 0, renewed_p_code);