diff options
author | Erlang/OTP <[email protected]> | 2010-03-22 14:51:35 +0000 |
---|---|---|
committer | Erlang/OTP <[email protected]> | 2010-03-22 15:56:43 +0100 |
commit | a4b4d36cdbbd692123443a1d3f19f1c452963201 (patch) | |
tree | 218002c4a2b38f69eb67d6388c8034820812fd47 /erts/emulator/beam/io.c | |
parent | 835dba95f810cc925ac856171014b9dbea8c90be (diff) | |
parent | 24095d93fb28bdd60abe771a426e39c6ff0f72c7 (diff) | |
download | otp-a4b4d36cdbbd692123443a1d3f19f1c452963201.tar.gz otp-a4b4d36cdbbd692123443a1d3f19f1c452963201.tar.bz2 otp-a4b4d36cdbbd692123443a1d3f19f1c452963201.zip |
Merge branch 'pan/otp_8332_halfword' into dev
* pan/otp_8332_halfword:
Teach testcase in driver_suite the new prototype for driver_async
wx: Correct usage of driver callbacks from wx thread
Adopt the new (R13B04) Nif functionality to the halfword codebase
Support monitoring and demonitoring from driver threads
Fix further test-suite problems
Correct the VM to work for more test suites
Teach {wordsize,internal|external} to system_info/1
Make tracing and distribution work
Turn on instruction packing in the loader and virtual machine
Add the BeamInstr data type for loaded BEAM code
Fix the BEAM dissambler for the half-word emulator
Store pointers to heap data in 32-bit words
Add a custom mmap wrapper to force heaps into the lower address range
Fit all heap data into the 32-bit address range
Diffstat (limited to 'erts/emulator/beam/io.c')
-rw-r--r-- | erts/emulator/beam/io.c | 234 |
1 files changed, 217 insertions, 17 deletions
diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index 3309b77086..10f1082039 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -72,6 +72,15 @@ erts_driver_t fd_driver; static int init_driver(erts_driver_t *, ErlDrvEntry *, DE_Handle *); static void terminate_port(Port *p); static void pdl_init(void); +#ifdef ERTS_SMP +static void driver_monitor_lock_pdl(Port *p); +static void driver_monitor_unlock_pdl(Port *p); +#define DRV_MONITOR_LOCK_PDL(Port) driver_monitor_lock_pdl(Port) +#define DRV_MONITOR_UNLOCK_PDL(Port) driver_monitor_unlock_pdl(Port) +#else +#define DRV_MONITOR_LOCK_PDL(Port) /* nothing */ +#define DRV_MONITOR_UNLOCK_PDL(Port) /* nothing */ +#endif static ERTS_INLINE ErlIOQueue* drvport2ioq(ErlDrvPort drvport) @@ -1998,12 +2007,13 @@ erts_do_exit_port(Port *p, Eterm from, Eterm reason) p->nlinks = NULL; erts_sweep_links(lnk, &sweep_one_link, &sc); } + DRV_MONITOR_LOCK_PDL(p); { ErtsMonitor *moni = p->monitors; p->monitors = NULL; erts_sweep_monitors(moni, &sweep_one_monitor, NULL); } - + DRV_MONITOR_UNLOCK_PDL(p); if ((p->status & ERTS_PORT_SFLG_DISTRIBUTION) && p->dist_entry) { erts_do_net_exits(p->dist_entry, rreason); @@ -3536,6 +3546,32 @@ static ERTS_INLINE void pdl_destroy(ErlDrvPDL pdl) erts_free(ERTS_ALC_T_PORT_DATA_LOCK, pdl); } +#ifdef ERTS_SMP + +static void driver_monitor_lock_pdl(Port *p) { + if (p->port_data_lock) { + driver_pdl_lock(p->port_data_lock); + } + /* Now we either have the port lock or the port_data_lock */ + ERTS_LC_ASSERT(!p->port_data_lock + || erts_lc_mtx_is_locked(&(p->port_data_lock->mtx))); + ERTS_SMP_LC_ASSERT(p->port_data_lock + || erts_lc_is_port_locked(p)); +} + +static void driver_monitor_unlock_pdl(Port *p) { + /* We should either have the port lock or the port_data_lock */ + ERTS_LC_ASSERT(!p->port_data_lock + || erts_lc_mtx_is_locked(&(p->port_data_lock->mtx))); + ERTS_SMP_LC_ASSERT(p->port_data_lock + || erts_lc_is_port_locked(p)); + if (p->port_data_lock) { + driver_pdl_unlock(p->port_data_lock); + } +} + +#endif + /* * exported driver_pdl_* functions ... */ @@ -3994,7 +4030,7 @@ drv_cancel_timer(Port *prt) erts_port_task_abort(prt->id, &prt->timeout_task); } -int driver_set_timer(ErlDrvPort ix, Uint t) +int driver_set_timer(ErlDrvPort ix, UWord t) { Port* prt = erts_drvport2port(ix); @@ -4053,12 +4089,16 @@ driver_read_timer(ErlDrvPort ix, unsigned long* t) int driver_get_now(ErlDrvNowData *now_data) { + Uint mega,secs,micro; ERTS_SMP_CHK_NO_PROC_LOCKS; if (now_data == NULL) { return -1; } - get_now(&(now_data->megasecs),&(now_data->secs),&(now_data->microsecs)); + get_now(&mega,&secs,µ); + now_data->megasecs = (unsigned long) mega; + now_data->secs = (unsigned long) secs; + now_data->microsecs = (unsigned long) micro; return 0; } @@ -4072,14 +4112,15 @@ static void ref_to_driver_monitor(Eterm ref, ErlDrvMonitor *mon) memcpy(mon,refp,sizeof(RefThing)); } -int driver_monitor_process(ErlDrvPort port, - ErlDrvTermData process, - ErlDrvMonitor *monitor) + +static int do_driver_monitor_process(Port *prt, + Eterm *buf, + ErlDrvTermData process, + ErlDrvMonitor *monitor) { - Port *prt = erts_drvport2port(port); Process *rp; Eterm ref; - Eterm buf[REF_THING_SIZE]; + if (prt->drv_ptr->process_exit == NULL) { return -1; } @@ -4089,22 +4130,76 @@ int driver_monitor_process(ErlDrvPort port, if (!rp) { return 1; } + ref = erts_make_ref_in_buffer(buf); erts_add_monitor(&(prt->monitors), MON_ORIGIN, ref, rp->id, NIL); erts_add_monitor(&(rp->monitors), MON_TARGET, ref, prt->id, NIL); - + erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_LINK); ref_to_driver_monitor(ref,monitor); return 0; } -int driver_demonitor_process(ErlDrvPort port, - const ErlDrvMonitor *monitor) +/* + * This can be called from a non scheduler thread iff a port_data_lock exists + */ +int driver_monitor_process(ErlDrvPort port, + ErlDrvTermData process, + ErlDrvMonitor *monitor) +{ + Port *prt; + int ret; + Uint32 status; + ErtsSchedulerData *sched = erts_get_scheduler_data(); + int ix = (int) port; + if (ix < 0 || erts_max_ports <= ix) { + return -1; + } + prt = &erts_port[ix]; + + DRV_MONITOR_LOCK_PDL(prt); + + if (sched) { + status = erts_port[ix].status; + } else { + erts_smp_port_state_lock(prt); + status = erts_port[ix].status; + erts_smp_port_state_unlock(prt); + } + + if (status & ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP) { + DRV_MONITOR_UNLOCK_PDL(prt); + return -1; + } + + /* Now (in SMP) we should have either the port lock (if we have a scheduler) or the port data lock + (if we're a driver thread) */ + ERTS_SMP_LC_ASSERT((sched != NULL || prt->port_data_lock)); + +#if !HEAP_ON_C_STACK + if (!sched) { + /* Need a separate allocation for the ref :( */ + Eterm *buf = erts_alloc(ERTS_ALC_T_TEMP_TERM, + sizeof(Eterm)*REF_THING_SIZE); + ret = do_driver_monitor_process(prt,buf,process,monitor); + erts_free(ERTS_ALC_T_TEMP_TERM,buf); + } else +#endif + { + DeclareTmpHeapNoproc(buf,REF_THING_SIZE); + UseTmpHeapNoproc(REF_THING_SIZE); + ret = do_driver_monitor_process(prt,buf,process,monitor); + UnUseTmpHeapNoproc(REF_THING_SIZE); + } + DRV_MONITOR_UNLOCK_PDL(prt); + return ret; +} + +static int do_driver_demonitor_process(Port *prt, Eterm *buf, + const ErlDrvMonitor *monitor) { - Port *prt = erts_drvport2port(port); Process *rp; Eterm ref; - Eterm buf[REF_THING_SIZE]; ErtsMonitor *mon; Eterm to; @@ -4137,12 +4232,60 @@ int driver_demonitor_process(ErlDrvPort port, return 0; } -ErlDrvTermData driver_get_monitored_process(ErlDrvPort port, +int driver_demonitor_process(ErlDrvPort port, + const ErlDrvMonitor *monitor) +{ + Port *prt; + int ret; + Uint32 status; + ErtsSchedulerData *sched = erts_get_scheduler_data(); + int ix = (int) port; + if (ix < 0 || erts_max_ports <= ix) { + return -1; + } + prt = &erts_port[ix]; + + DRV_MONITOR_LOCK_PDL(prt); + + if (sched) { + status = erts_port[ix].status; + } else { + erts_smp_port_state_lock(prt); + status = erts_port[ix].status; + erts_smp_port_state_unlock(prt); + } + + if (status & ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP) { + DRV_MONITOR_UNLOCK_PDL(prt); + return -1; + } + + /* Now we should have either the port lock (if we have a scheduler) or the port data lock + (if we're a driver thread) */ + ERTS_SMP_LC_ASSERT((sched != NULL || prt->port_data_lock)); +#if !HEAP_ON_C_STACK + if (!sched) { + /* Need a separate allocation for the ref :( */ + Eterm *buf = erts_alloc(ERTS_ALC_T_TEMP_TERM, + sizeof(Eterm)*REF_THING_SIZE); + ret = do_driver_demonitor_process(prt,buf,monitor); + erts_free(ERTS_ALC_T_TEMP_TERM,buf); + } else +#endif + { + DeclareTmpHeapNoproc(buf,REF_THING_SIZE); + UseTmpHeapNoproc(REF_THING_SIZE); + ret = do_driver_demonitor_process(prt,buf,monitor); + UnUseTmpHeapNoproc(REF_THING_SIZE); + } + DRV_MONITOR_UNLOCK_PDL(prt); + return ret; +} + +static ErlDrvTermData do_driver_get_monitored_process(Port *prt, Eterm *buf, const ErlDrvMonitor *monitor) { - Port *prt = erts_drvport2port(port); Eterm ref; - Eterm buf[REF_THING_SIZE]; ErtsMonitor *mon; Eterm to; @@ -4158,6 +4301,59 @@ ErlDrvTermData driver_get_monitored_process(ErlDrvPort port, return (ErlDrvTermData) to; } + +ErlDrvTermData driver_get_monitored_process(ErlDrvPort port, + const ErlDrvMonitor *monitor) +{ + Port *prt; + ErlDrvTermData ret; + Uint32 status; + ErtsSchedulerData *sched = erts_get_scheduler_data(); + int ix = (int) port; + if (ix < 0 || erts_max_ports <= ix) { + return driver_term_nil; + } + prt = &erts_port[ix]; + + DRV_MONITOR_LOCK_PDL(prt); + + if (sched) { + status = erts_port[ix].status; + } else { + erts_smp_port_state_lock(prt); + status = erts_port[ix].status; + erts_smp_port_state_unlock(prt); + } + + if (status & ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP) { + DRV_MONITOR_UNLOCK_PDL(prt); + return driver_term_nil; + } + + /* Now we should have either the port lock (if we have a scheduler) or the port data lock + (if we're a driver thread) */ + ERTS_SMP_LC_ASSERT((sched != NULL || prt->port_data_lock)); + +#if !HEAP_ON_C_STACK + if (!sched) { + /* Need a separate allocation for the ref :( */ + Eterm *buf = erts_alloc(ERTS_ALC_T_TEMP_TERM, + sizeof(Eterm)*REF_THING_SIZE); + ret = do_driver_get_monitored_process(prt,buf,monitor); + erts_free(ERTS_ALC_T_TEMP_TERM,buf); + } else +#endif + { + DeclareTmpHeapNoproc(buf,REF_THING_SIZE); + UseTmpHeapNoproc(REF_THING_SIZE); + ret = do_driver_get_monitored_process(prt,buf,monitor); + UnUseTmpHeapNoproc(REF_THING_SIZE); + } + DRV_MONITOR_UNLOCK_PDL(prt); + return ret; +} + + int driver_compare_monitors(const ErlDrvMonitor *monitor1, const ErlDrvMonitor *monitor2) { @@ -4173,18 +4369,22 @@ void erts_fire_port_monitor(Port *prt, Eterm ref) ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt)); ASSERT(prt->drv_ptr != NULL); - + DRV_MONITOR_LOCK_PDL(prt); if (erts_lookup_monitor(prt->monitors,ref) == NULL) { + DRV_MONITOR_UNLOCK_PDL(prt); return; } callback = prt->drv_ptr->process_exit; ASSERT(callback != NULL); ref_to_driver_monitor(ref,&drv_monitor); + DRV_MONITOR_UNLOCK_PDL(prt); fpe_was_unmasked = erts_block_fpe(); (*callback)((ErlDrvData) (prt->drv_data), &drv_monitor); erts_unblock_fpe(fpe_was_unmasked); + DRV_MONITOR_LOCK_PDL(prt); /* remove monitor *after* callback */ rmon = erts_remove_monitor(&(prt->monitors),ref); + DRV_MONITOR_UNLOCK_PDL(prt); if (rmon) { erts_destroy_monitor(rmon); } |