Ejemplo n.º 1
0
Export *
erts_suspend_process_on_pending_purge_lambda(Process *c_p)
{
    erts_smp_mtx_lock(&purge_state.mtx);
    if (is_value(purge_state.module)) {
	/*
	 * The process c_p is about to call a fun in the code
	 * that we are trying to purge. Suspend it and call
	 * erts_code_purger:pending_purge_lambda/3. The process
	 * will be resumed when the purge completes or aborts,
	 * and will then try to do the call again.
	 */
	if (purge_state.sp_ix >= purge_state.sp_size) {
	    Eterm *sprocs;
	    purge_state.sp_size += 100;
	    sprocs = erts_alloc(ERTS_ALC_T_PURGE_DATA,
				(sizeof(ErlFunEntry *)
				 * purge_state.sp_size));
	    sys_memcpy((void *) sprocs,
		       (void *) purge_state.sprocs,
		       purge_state.sp_ix*sizeof(ErlFunEntry *));
	    if (purge_state.sprocs != &purge_state.def_sprocs[0])
		erts_free(ERTS_ALC_T_PURGE_DATA, purge_state.sprocs);
	    purge_state.sprocs = sprocs;
	}
	purge_state.sprocs[purge_state.sp_ix++] = c_p->common.id;
	erts_suspend(c_p, ERTS_PROC_LOCK_MAIN, NULL);
	ERTS_VBUMP_ALL_REDS(c_p);
    }
    erts_smp_mtx_unlock(&purge_state.mtx);
    return purge_state.pending_purge_lambda;
}
Ejemplo n.º 2
0
Archivo: erl_flxctr.c Proyecto: bmk/otp
static void suspend_until_thr_prg(Process* p)
{
    Binary* state_bin;
    ErtsFlxCtrWakeUpLaterInfo* info;
    state_bin = erts_create_magic_binary(sizeof(ErtsFlxCtrWakeUpLaterInfo),
                                         erts_flxctr_wait_dtor);
    info = ERTS_MAGIC_BIN_DATA(state_bin);
    info->process = p;
    erts_refc_inctest(&state_bin->intern.refc, 1);
    erts_suspend(p, ERTS_PROC_LOCK_MAIN, NULL);
    erts_proc_inc_refc(p);
    ERTS_VBUMP_ALL_REDS(p);
    erts_schedule_thr_prgr_later_op(thr_prg_wake_up_later, state_bin, &info->later_op);
}
Ejemplo n.º 3
0
NifExport *
erts_nif_export_schedule(Process *c_p, Process *dirty_shadow_proc,
			 ErtsCodeMFA *mfa, BeamInstr *pc,
			 BeamInstr instr,
			 void *dfunc, void *ifunc,
			 Eterm mod, Eterm func,
			 int argc, const Eterm *argv)
{
    Process *used_proc;
    ErtsSchedulerData *esdp;
    Eterm* reg;
    NifExport* nep;
    int i;

    ERTS_SMP_LC_ASSERT(erts_proc_lc_my_proc_locks(c_p)
		       & ERTS_PROC_LOCK_MAIN);

    if (dirty_shadow_proc) {
	esdp = erts_get_scheduler_data();
	ASSERT(esdp && ERTS_SCHEDULER_IS_DIRTY(esdp));

	used_proc = dirty_shadow_proc;
    }
    else {
	esdp = erts_proc_sched_data(c_p);
	ASSERT(esdp && !ERTS_SCHEDULER_IS_DIRTY(esdp));

	used_proc = c_p;
	ERTS_VBUMP_ALL_REDS(c_p);
    }

    reg = esdp->x_reg_array;

    if (mfa)
	nep = erts_get_proc_nif_export(c_p, (int) mfa->arity);
    else {
	/* If no mfa, this is not the first schedule... */
	nep = ERTS_PROC_GET_NIF_TRAP_EXPORT(c_p);
	ASSERT(nep && nep->argc >= 0);
    }

    if (nep->argc < 0) {
	/*
	 * First schedule; save things that might
	 * need to be restored...
	 */
	for (i = 0; i < (int) mfa->arity; i++)
	    nep->argv[i] = reg[i];
	nep->pc = pc;
	nep->cp = c_p->cp;
	nep->mfa = mfa;
	nep->current = c_p->current;
	ASSERT(argc >= 0);
	nep->argc = (int) mfa->arity;
	nep->m = NULL;

	ASSERT(!erts_check_nif_export_in_area(c_p,
					      (char *) nep,
					      (sizeof(NifExport)
					       + (sizeof(Eterm)
						  *(nep->argc-1)))));
    }
    /* Copy new arguments into register array if necessary... */
    if (reg != argv) {
	for (i = 0; i < argc; i++)
	    reg[i] = argv[i];
    }
    ASSERT(is_atom(mod) && is_atom(func));
    nep->exp.info.mfa.module = mod;
    nep->exp.info.mfa.function = func;
    nep->exp.info.mfa.arity = (Uint) argc;
    nep->exp.beam[0] = (BeamInstr) instr; /* call_nif || apply_bif */
    nep->exp.beam[1] = (BeamInstr) dfunc;
    nep->func = ifunc;
    used_proc->arity = argc;
    used_proc->freason = TRAP;
    used_proc->i = (BeamInstr*) nep->exp.addressv[0];
    return nep;
}
Ejemplo n.º 4
0
Process *hipe_mode_switch(Process *p, unsigned cmd, Eterm reg[])
{
    unsigned result;
    Eterm reds_in = p->def_arg_reg[5];
    /*
     * Process is in the normal case scheduled out when reduction
     * count reach zero. When "save calls" is enabled reduction
     * count is subtracted with CONTEXT_REDS, i.e. initial reduction
     * count will be zero or less and process is scheduled out
     * when -CONTEXT_REDS is reached.
     *
     * HiPE does not support the "save calls" feature, so we switch
     * to using a positive reduction counter when executing in
     * hipe mode, but need to restore the "save calls" when
     * returning to beam. We also need to hide the save calls buffer
     * from BIFs. We do that by moving the saved calls buf to
     * suspended saved calls buf.
     *
     * Beam has initial reduction count in stored in p->def_arg_reg[5].
     *
     * Beam expects -neg_o_reds to be found in p->def_arg_reg[4]
     * on return to beam.
     */

    {
	struct saved_calls *scb = ERTS_PROC_SET_SAVED_CALLS_BUF(p, NULL);
	if (scb) {
	    reds_in += CONTEXT_REDS;
	    p->fcalls += CONTEXT_REDS;
	    ERTS_PROC_SET_SUSPENDED_SAVED_CALLS_BUF(p, scb);
	}
    }

    p->flags |= F_HIPE_MODE; /* inform bifs where we are comming from... */

    p->i = NULL;
    /* Set current_function to undefined. stdlib hibernate tests rely on it. */
    p->current = NULL;

    DPRINTF("cmd == %#x (%s)", cmd, code_str(cmd));
    HIPE_CHECK_PCB(p);
    p->arity = 0;
    switch (cmd & 0xFF) {
      case HIPE_MODE_SWITCH_CMD_CALL: {
	  /* BEAM calls a native code function */
	  unsigned arity = cmd >> 8;

	  /* p->hipe.u.ncallee set in beam_emu */
	  if (p->cp == hipe_beam_pc_return) {
	    /* Native called BEAM, which now tailcalls native. */
	    hipe_pop_beam_trap_frame(p);
	    result = hipe_tailcall_to_native(p, arity, reg);
	    break;
	  }
	  DPRINTF("calling %#lx/%u", (long)p->hipe.u.ncallee, arity);
	  result = hipe_call_to_native(p, arity, reg);
	  break;
      }
      case HIPE_MODE_SWITCH_CMD_CALL_CLOSURE: {
	  /* BEAM calls a native code closure */
	  unsigned arity = cmd >> 8; /* #formals + #fvs (closure not counted) */
	  Eterm fun;
	  ErlFunThing *funp;

	  /* drop the fvs, move the closure, correct arity */
	  fun = reg[arity];
	  HIPE_ASSERT(is_fun(fun));
	  funp = (ErlFunThing*)fun_val(fun);
	  HIPE_ASSERT(funp->num_free <= arity);
	  arity -= funp->num_free;	/* arity == #formals */
	  reg[arity] = fun;
	  ++arity;	/* correct for having added the closure */
	  /* HIPE_ASSERT(p->hipe.u.ncallee == (void(*)(void))funp->native_address); */

	  /* just like a normal call from now on */

	  /* p->hipe.u.ncallee set in beam_emu */
	  if (p->cp == hipe_beam_pc_return) {
	      /* Native called BEAM, which now tailcalls native. */
	      hipe_pop_beam_trap_frame(p);
	      result = hipe_tailcall_to_native(p, arity, reg);
	      break;
	  }
	  DPRINTF("calling %#lx/%u", (long)p->hipe.u.ncallee, arity);
	  result = hipe_call_to_native(p, arity, reg);
	  break;
      }
      case HIPE_MODE_SWITCH_CMD_THROW: {
	  /* BEAM just executed hipe_beam_pc_throw[] */
	  /* Native called BEAM, which now throws an exception back to native. */
	  DPRINTF("beam throws freason %#lx fvalue %#lx", p->freason, p->fvalue);
	  hipe_pop_beam_trap_frame(p);
      do_throw_to_native:
	  p->def_arg_reg[0] = exception_tag[GET_EXC_CLASS(p->freason)];
	  hipe_find_handler(p);
	  result = hipe_throw_to_native(p);
	  break;
      }
      case HIPE_MODE_SWITCH_CMD_RETURN: {
	  /* BEAM just executed hipe_beam_pc_return[] */
	  /* Native called BEAM, which now returns back to native. */
	  /* pop trap frame off estack */
	  hipe_pop_beam_trap_frame(p);
	  p->def_arg_reg[0] = reg[0];
	  result = hipe_return_to_native(p);
	  break;
      }
    do_resume:
      case HIPE_MODE_SWITCH_CMD_RESUME: {
	  /* BEAM just executed hipe_beam_pc_resume[] */
	  /* BEAM called native, which suspended. */
	  if (p->flags & F_TIMO) {
	      /* XXX: The process will immediately execute 'clear_timeout',
		 repeating these two statements. Remove them? */
	      p->flags &= ~F_TIMO;
	      JOIN_MESSAGE(p);
	      p->def_arg_reg[0] = 0;	/* make_small(0)? */
	  } else
	      p->def_arg_reg[0] = 1;	/* make_small(1)? */
	  result = hipe_return_to_native(p);
	  break;
      }
      default:
	erts_exit(ERTS_ERROR_EXIT, "hipe_mode_switch: cmd %#x\r\n", cmd);
    }
 do_return_from_native:
    DPRINTF("result == %#x (%s)", result, code_str(result));
    HIPE_CHECK_PCB(p);
    switch (result) {
      case HIPE_MODE_SWITCH_RES_RETURN: {
	  hipe_return_from_native(p);
	  reg[0] = p->def_arg_reg[0];
	  DPRINTF("returning with r(0) == %#lx", reg[0]);
	  break;
      }
      case HIPE_MODE_SWITCH_RES_THROW: {
	  DPRINTF("native throws freason %#lx fvalue %#lx", p->freason, p->fvalue);
	  hipe_throw_from_native(p);
	  break;
      }
      case HIPE_MODE_SWITCH_RES_TRAP: {
	  /*
	   * Native code called a BIF, which "failed" with a TRAP to BEAM.
	   * Prior to returning, the BIF stored (see BIF_TRAP<N>):

	   * the callee's address in p->i
	   * the callee's parameters in reg[0..2]
	   * the callee's arity in p->arity (for BEAM gc purposes)
	   *
	   * We need to remove the BIF's parameters from the native
	   * stack: to this end hipe_${ARCH}_glue.S stores the BIF's
	   * arity in p->hipe.narity.
	   *
	   * If the BIF emptied the stack (typically hibernate), p->hipe.nstack
	   * is NULL and there is no need to get rid of stacked parameters.
	   */
	  unsigned int i, is_recursive = 0;

          if (p->hipe.nstack != NULL) {
	      ASSERT(p->hipe.nsp != NULL);
	      is_recursive = hipe_trap_from_native_is_recursive(p);
          }
	  else {
	      /* Some architectures (risc) need this re-reset of nsp as the
	       * BIF wrapper do not detect stack change and causes an obsolete
	       * stack pointer to be saved in p->hipe.nsp before return to us.
	       */
	      p->hipe.nsp = NULL;
	  }

	  /* Schedule next process if current process was hibernated or is waiting
	     for messages */
	  if (p->flags & F_HIBERNATE_SCHED) {
	      p->flags &= ~F_HIBERNATE_SCHED;
	      goto do_schedule;
	  }

	  if (!(erts_atomic32_read_acqb(&p->state) & ERTS_PSFLG_ACTIVE)) {
	      for (i = 0; i < p->arity; ++i)
		  p->arg_reg[i] = reg[i]; 	      
	      goto do_schedule;
	  }

	  if (is_recursive)
	      hipe_push_beam_trap_frame(p, reg, p->arity);
	  
	  result = HIPE_MODE_SWITCH_RES_CALL_BEAM;
	  break;
      }
      case HIPE_MODE_SWITCH_RES_CALL_EXPORTED: {
	  /* Native code calls or tailcalls BEAM.
	   *
	   * p->hipe.u.callee_exp is the callee's export entry
	   * p->arity is the callee's arity
	   * p->def_arg_reg[] contains the register parameters
	   * p->hipe.nsp[] contains the stacked parameters
	   */
	  if (hipe_call_from_native_is_recursive(p, reg)) {
	      /* BEAM called native, which now calls BEAM */
	      hipe_push_beam_trap_frame(p, reg, p->arity);
	  }
	  break;
      }
      case HIPE_MODE_SWITCH_RES_CALL_CLOSURE: {
	  /* Native code calls or tailcalls a closure in BEAM
	   *
	   * In native code a call to a closure of arity n looks like
	   * F(A1, ..., AN, Closure),
	   * The BEAM code for a closure expects to get:
	   * F(A1, ..., AN, FV1, ..., FVM, Closure)
	   *  (Where Ai is argument i and FVj is free variable j)
	   *
	   * p->hipe.u.closure contains the closure
	   * p->def_arg_reg[] contains the register parameters
	   * p->hipe.nsp[] contains the stacked parameters
	   */
	  ErlFunThing *closure;
	  unsigned num_free, arity, i, is_recursive;

	  HIPE_ASSERT(is_fun(p->hipe.u.closure));
	  closure = (ErlFunThing*)fun_val(p->hipe.u.closure);
	  num_free = closure->num_free;
	  arity = closure->fe->arity;

	  /* Store the arity in p->arity for the stack popping. */
	  /* Note: we already have the closure so only need to move arity
	     values to reg[]. However, there are arity+1 parameters in the
	     native code state that need to be removed. */
	  p->arity = arity+1; /* +1 for the closure */

	  /* Get parameters, don't do GC just yet. */
	  is_recursive = hipe_call_from_native_is_recursive(p, reg);

	  if ((Sint)closure->fe->address[-1] < 0) {
	      /* Unloaded. Let beam_emu.c:call_fun() deal with it. */
	      result = HIPE_MODE_SWITCH_RES_CALL_CLOSURE;
	  } else {
	      /* The BEAM code is present. Prepare to call it. */

	      /* Append the free vars after the actual parameters. */
	      for (i = 0; i < num_free; ++i)
		  reg[arity+i] = closure->env[i];

	      /* Update arity to reflect the new parameters. */
	      arity += i;

	      /* Make a call to the closure's BEAM code. */
	      p->i = closure->fe->address;

	      /* Change result code to the faster plain CALL type. */
	      result = HIPE_MODE_SWITCH_RES_CALL_BEAM;
	  }
	  /* Append the closure as the last parameter. Don't increment arity. */
	  reg[arity] = p->hipe.u.closure;

	  if (is_recursive) {
	      /* BEAM called native, which now calls BEAM.
		 Need to put a trap-frame on the beam stack.
		 This may cause GC, which is safe now that
		 the arguments, free vars, and most
		 importantly the closure, all are in reg[]. */
	      hipe_push_beam_trap_frame(p, reg, arity+1);
	  }
	  break;
      }
      case HIPE_MODE_SWITCH_RES_SUSPEND: {
	  p->i = hipe_beam_pc_resume;
	  p->arity = 0;
	  goto do_schedule;
      }
      case HIPE_MODE_SWITCH_RES_WAIT:
      case HIPE_MODE_SWITCH_RES_WAIT_TIMEOUT: {
	  /* same semantics, different debug trace messages */
	  /* XXX: BEAM has different entries for the locked and unlocked
	     cases. HiPE doesn't, so we must check dynamically. */
	  if (p->flags & F_HIPE_RECV_LOCKED)
	      p->flags &= ~F_HIPE_RECV_LOCKED;
	  else
	      erts_proc_lock(p, ERTS_PROC_LOCKS_MSG_RECEIVE);
	  p->i = hipe_beam_pc_resume;
	  p->arity = 0;
          if (erts_atomic32_read_nob(&p->state) & ERTS_PSFLG_EXITING)
              ASSERT(erts_atomic32_read_nob(&p->state) & ERTS_PSFLG_ACTIVE);
          else if (!(p->flags & F_HIPE_RECV_YIELD))
              erts_atomic32_read_band_relb(&p->state, ~ERTS_PSFLG_ACTIVE);
          else {
              /* Yielded from receive */
              ERTS_VBUMP_ALL_REDS(p);
              p->flags &= ~F_HIPE_RECV_YIELD;
          }
	  erts_proc_unlock(p, ERTS_PROC_LOCKS_MSG_RECEIVE);
      do_schedule:
	  {
	      struct saved_calls *scb;

	      scb = ERTS_PROC_SET_SUSPENDED_SAVED_CALLS_BUF(p, NULL);
	      if (scb)
		  ERTS_PROC_SET_SAVED_CALLS_BUF(p, scb);

              /* The process may have died while it was executing,
                 if so we return out from native code to the interpreter */
              if (erts_atomic32_read_nob(&p->state) & ERTS_PSFLG_EXITING)
                  p->i = beam_exit;
#ifdef DEBUG
	      ASSERT(p->debug_reds_in == reds_in);
#endif
	      p->flags &= ~F_HIPE_MODE;

	      ERTS_UNREQ_PROC_MAIN_LOCK(p);
	      p = erts_schedule(NULL, p, reds_in - p->fcalls);
	      ERTS_REQ_PROC_MAIN_LOCK(p);
	      ASSERT(!(p->flags & F_HIPE_MODE));
	      p->flags &= ~F_HIPE_RECV_LOCKED;
	      reg = p->scheduler_data->x_reg_array;
	  }
	  {
	      Eterm *argp;
	      int i;
	  
	      argp = p->arg_reg;
	      for (i = p->arity; --i >= 0;)
		  reg[i] = argp[i];
	  }
	  {
	      struct saved_calls *scb;

	      reds_in = p->fcalls;
	      p->def_arg_reg[5] = reds_in;
#ifdef DEBUG
	      p->debug_reds_in = reds_in;
#endif
	      if (p->i == hipe_beam_pc_resume) {
		  p->flags |= F_HIPE_MODE; /* inform bifs where we are comming from... */
		  scb = ERTS_PROC_SET_SAVED_CALLS_BUF(p, NULL);
		  if (scb)
		      ERTS_PROC_SET_SUSPENDED_SAVED_CALLS_BUF(p, scb);
		  p->i = NULL;
		  p->arity = 0;
		  goto do_resume;
	      }

	      scb = ERTS_PROC_GET_SAVED_CALLS_BUF(p);
	      if (!scb)
		  p->def_arg_reg[4] = 0;
	      else {
		  p->def_arg_reg[4] = CONTEXT_REDS;
		  p->fcalls = -CONTEXT_REDS + reds_in;
	      }
	  }

	  HIPE_CHECK_PCB(p);
	  result = HIPE_MODE_SWITCH_RES_CALL_BEAM;
	  p->def_arg_reg[3] = result;
	  return p;
      }
      case HIPE_MODE_SWITCH_RES_APPLY: {
	  Eterm mfa[3], args;
	  unsigned int arity;
	  void *address;

	  hipe_pop_params(p, 3, &mfa[0]);

	  /* Unroll the arglist onto reg[]. */
	  args = mfa[2];
	  arity = 0;
	  while (is_list(args)) {
	      if (arity < 255) {
		  reg[arity++] = CAR(list_val(args));
		  args = CDR(list_val(args));
	      } else
		  goto do_apply_fail;
	  }
	  if (is_not_nil(args))
	      goto do_apply_fail;

	  /* find a native code entry point for {M,F,A} for a remote call */
	  address = hipe_get_remote_na(mfa[0], mfa[1], arity);
	  if (!address)
		  goto do_apply_fail;
	  p->hipe.u.ncallee = (void(*)(void)) address;
	  result = hipe_tailcall_to_native(p, arity, reg);
	  goto do_return_from_native;
      do_apply_fail:
	  p->freason = BADARG;
	  goto do_throw_to_native;
      }
      default:
	erts_exit(ERTS_ERROR_EXIT, "hipe_mode_switch: result %#x\r\n", result);
    }

    {
	struct saved_calls *scb = ERTS_PROC_SET_SUSPENDED_SAVED_CALLS_BUF(p, NULL);
	if (!scb)
	    p->def_arg_reg[4] = 0;
	else {
	    p->def_arg_reg[4] = CONTEXT_REDS;
	    p->fcalls -= CONTEXT_REDS;
	    ERTS_PROC_SET_SAVED_CALLS_BUF(p, scb);
	}
    }

    HIPE_CHECK_PCB(p);
    p->def_arg_reg[3] = result;
#if NR_ARG_REGS > 5
    /*
     * When NR_ARG_REGS > 5, we need to protect the process' input
     * reduction count (which BEAM stores in def_arg_reg[5]) from
     * being clobbered by the arch glue code.
     */
    p->def_arg_reg[5] = reds_in;
#endif
    p->flags &= ~F_HIPE_MODE;
    return p;
}