diff options
Diffstat (limited to 'erts/emulator')
-rw-r--r-- | erts/emulator/beam/atom.names | 1 | ||||
-rwxr-xr-x[-rw-r--r--] | erts/emulator/beam/erl_bif_info.c | 96 | ||||
-rw-r--r-- | erts/emulator/beam/erl_lock_count.c | 29 | ||||
-rw-r--r-- | erts/emulator/beam/erl_lock_count.h | 3 | ||||
-rw-r--r-- | erts/emulator/beam/erl_process.h | 39 | ||||
-rw-r--r-- | erts/emulator/beam/erl_process_lock.c | 28 | ||||
-rw-r--r-- | erts/emulator/beam/erl_process_lock.h | 2 | ||||
-rwxr-xr-x[-rw-r--r--] | erts/emulator/beam/global.h | 5 | ||||
-rw-r--r-- | erts/emulator/beam/io.c | 42 | ||||
-rw-r--r-- | erts/emulator/sys/unix/sys.c | 2 | ||||
-rwxr-xr-x | erts/emulator/sys/win32/sys.c | 17 | ||||
-rw-r--r-- | erts/emulator/test/port_bif_SUITE.erl | 391 |
12 files changed, 418 insertions, 237 deletions
diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names index 02735d4b68..78c566ed38 100644 --- a/erts/emulator/beam/atom.names +++ b/erts/emulator/beam/atom.names @@ -391,6 +391,7 @@ atom opt atom or atom ordered_set atom orelse +atom os_pid atom os_type atom os_version atom ose_bg_proc diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index f889ccdb93..2373dc7af4 100644..100755 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -2765,7 +2765,8 @@ port_info_1(BIF_ALIST_1) am_id, am_connected, am_input, - am_output + am_output, + am_os_pid }; Eterm items[ASIZE(keys)]; Eterm result = NIL; @@ -2822,6 +2823,7 @@ port_info_1(BIF_ALIST_1) ** name String ** input Number of bytes input from port program ** output Number of bytes output to the port program +** os_pid The child's process ID */ BIF_RETTYPE port_info_2(BIF_ALIST_2) @@ -2922,6 +2924,18 @@ static BIF_RETTYPE port_info(Process* p, Eterm portid, Eterm item) hp = HAlloc(p, hsz); res = erts_bld_uint(&hp, NULL, n); } + else if (item == am_os_pid) { + if (prt->os_pid >= 0) { + Uint hsz = 3; + UWord n = prt->os_pid; + (void) erts_bld_uword(NULL, &hsz, n); + hp = HAlloc(p, hsz); + res = erts_bld_uword(&hp, NULL, n); + } else { + hp = HAlloc(p, 3); + res = am_undefined; + } + } else if (item == am_registered_name) { RegProc *reg; reg = prt->reg; @@ -4110,48 +4124,52 @@ BIF_RETTYPE erts_debug_lock_counters_1(BIF_ALIST_1) Eterm* tp = tuple_val(BIF_ARG_1); switch (arityval(tp[0])) { - case 2: + case 2: { + int opt = 0; + int val = 0; if (ERTS_IS_ATOM_STR("copy_save", tp[1])) { - erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN); - erts_smp_thr_progress_block(); - if (tp[2] == am_true) { - - res = erts_lcnt_set_rt_opt(ERTS_LCNT_OPT_COPYSAVE) ? am_true : am_false; - - } else if (tp[2] == am_false) { - - res = erts_lcnt_clear_rt_opt(ERTS_LCNT_OPT_COPYSAVE) ? am_true : am_false; - - } else { - erts_smp_thr_progress_unblock(); - erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN); - BIF_ERROR(BIF_P, BADARG); - } - erts_smp_thr_progress_unblock(); - erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN); - BIF_RET(res); - + opt = ERTS_LCNT_OPT_COPYSAVE; } else if (ERTS_IS_ATOM_STR("process_locks", tp[1])) { - erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN); - erts_smp_thr_progress_block(); - if (tp[2] == am_true) { - - res = erts_lcnt_set_rt_opt(ERTS_LCNT_OPT_PROCLOCK) ? am_true : am_false; - - } else if (tp[2] == am_false) { - - res = erts_lcnt_set_rt_opt(ERTS_LCNT_OPT_PROCLOCK) ? am_true : am_false; + opt = ERTS_LCNT_OPT_PROCLOCK; + } else if (ERTS_IS_ATOM_STR("port_locks", tp[1])) { + opt = ERTS_LCNT_OPT_PORTLOCK; + } else if (ERTS_IS_ATOM_STR("suspend", tp[1])) { + opt = ERTS_LCNT_OPT_SUSPEND; + } else if (ERTS_IS_ATOM_STR("location", tp[1])) { + opt = ERTS_LCNT_OPT_LOCATION; + } else { + BIF_ERROR(BIF_P, BADARG); + } + if (tp[2] == am_true) { + val = 1; + } else if (tp[2] == am_false) { + val = 0; + } else { + BIF_ERROR(BIF_P, BADARG); + } - } else { - erts_smp_thr_progress_unblock(); - erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN); - BIF_ERROR(BIF_P, BADARG); + erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN); + erts_smp_thr_progress_block(); + + if (val) { + res = erts_lcnt_set_rt_opt(opt) ? am_true : am_false; + } else { + res = erts_lcnt_clear_rt_opt(opt) ? am_true : am_false; + } +#ifdef ERTS_SMP + if (res != tp[2]) { + if (opt == ERTS_LCNT_OPT_PORTLOCK) { + erts_lcnt_enable_io_lock_count(val); + } else if (opt == ERTS_LCNT_OPT_PROCLOCK) { + erts_lcnt_enable_proc_lock_count(val); } - erts_smp_thr_progress_unblock(); - erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN); - BIF_RET(res); - } - break; + } +#endif + erts_smp_thr_progress_unblock(); + erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN); + BIF_RET(res); + break; + } default: break; diff --git a/erts/emulator/beam/erl_lock_count.c b/erts/emulator/beam/erl_lock_count.c index a36c53560e..741c0cb08e 100644 --- a/erts/emulator/beam/erl_lock_count.c +++ b/erts/emulator/beam/erl_lock_count.c @@ -49,7 +49,7 @@ const char *str_undefined = "undefined"; static ethr_tsd_key lcnt_thr_data_key; static int lcnt_n_thr; -static erts_lcnt_thread_data_t *lcnt_thread_data[1024]; +static erts_lcnt_thread_data_t *lcnt_thread_data[4096]; /* local functions */ @@ -240,7 +240,7 @@ void erts_lcnt_init() { lcnt_lock(); - erts_lcnt_rt_options = ERTS_LCNT_OPT_PROCLOCK; + erts_lcnt_rt_options = ERTS_LCNT_OPT_PROCLOCK | ERTS_LCNT_OPT_LOCATION; eltd = lcnt_thread_data_alloc(); @@ -312,7 +312,7 @@ void erts_lcnt_list_insert(erts_lcnt_lock_list_t *list, erts_lcnt_lock_t *lock) } void erts_lcnt_list_delete(erts_lcnt_lock_list_t *list, erts_lcnt_lock_t *lock) { - + if (lock->next) lock->next->prev = lock->prev; if (lock->prev) lock->prev->next = lock->next; if (list->head == lock) list->head = lock->next; @@ -334,6 +334,10 @@ void erts_lcnt_init_lock(erts_lcnt_lock_t *lock, char *name, Uint16 flag ) { } void erts_lcnt_init_lock_x(erts_lcnt_lock_t *lock, char *name, Uint16 flag, Eterm id) { int i; + if (!name) { + lock->flag = 0; + return; + } lcnt_lock(); lock->next = NULL; @@ -363,6 +367,8 @@ void erts_lcnt_init_lock_x(erts_lcnt_lock_t *lock, char *name, Uint16 flag, Eter void erts_lcnt_destroy_lock(erts_lcnt_lock_t *lock) { erts_lcnt_lock_t *deleted_lock; + if (!ERTS_LCNT_LOCK_TYPE(lock)) return; + lcnt_lock(); if (erts_lcnt_rt_options & ERTS_LCNT_OPT_COPYSAVE) { @@ -378,6 +384,7 @@ void erts_lcnt_destroy_lock(erts_lcnt_lock_t *lock) { } /* delete original */ erts_lcnt_list_delete(erts_lcnt_data->current_locks, lock); + lock->flag = 0; lcnt_unlock(); } @@ -389,6 +396,7 @@ void erts_lcnt_lock_opt(erts_lcnt_lock_t *lock, Uint16 option) { erts_lcnt_thread_data_t *eltd; if (erts_lcnt_rt_options & ERTS_LCNT_OPT_SUSPEND) return; + if (!ERTS_LCNT_LOCK_TYPE(lock)) return; eltd = lcnt_get_thread_data(); @@ -422,6 +430,7 @@ void erts_lcnt_lock(erts_lcnt_lock_t *lock) { erts_lcnt_thread_data_t *eltd; if (erts_lcnt_rt_options & ERTS_LCNT_OPT_SUSPEND) return; + if (!ERTS_LCNT_LOCK_TYPE(lock)) return; w_state = ethr_atomic_read(&lock->w_state); ethr_atomic_inc( &lock->w_state); @@ -452,6 +461,7 @@ void erts_lcnt_lock_unaquire(erts_lcnt_lock_t *lock) { /* should check if this thread was "waiting" */ if (erts_lcnt_rt_options & ERTS_LCNT_OPT_SUSPEND) return; + if (!ERTS_LCNT_LOCK_TYPE(lock)) return; ethr_atomic_dec( &lock->w_state); } @@ -475,6 +485,7 @@ void erts_lcnt_lock_post_x(erts_lcnt_lock_t *lock, char *file, unsigned int line #endif if (erts_lcnt_rt_options & ERTS_LCNT_OPT_SUSPEND) return; + if (!ERTS_LCNT_LOCK_TYPE(lock)) return; #ifdef DEBUG if (!(lock->flag & (ERTS_LCNT_LT_RWMUTEX | ERTS_LCNT_LT_RWSPINLOCK))) { @@ -489,9 +500,13 @@ void erts_lcnt_lock_post_x(erts_lcnt_lock_t *lock, char *file, unsigned int line ASSERT(eltd); /* if lock was in conflict, time it */ - - stats = lcnt_get_lock_stats(lock, file, line); + if (erts_lcnt_rt_options & ERTS_LCNT_OPT_LOCATION) { + stats = lcnt_get_lock_stats(lock, file, line); + } else { + stats = &lock->stats[0]; + } + if (eltd->timer_set) { lcnt_time(&timer); @@ -510,6 +525,7 @@ void erts_lcnt_lock_post_x(erts_lcnt_lock_t *lock, char *file, unsigned int line void erts_lcnt_unlock_opt(erts_lcnt_lock_t *lock, Uint16 option) { if (erts_lcnt_rt_options & ERTS_LCNT_OPT_SUSPEND) return; + if (!ERTS_LCNT_LOCK_TYPE(lock)) return; if (option & ERTS_LCNT_LO_WRITE) ethr_atomic_dec(&lock->w_state); if (option & ERTS_LCNT_LO_READ ) ethr_atomic_dec(&lock->r_state); } @@ -520,6 +536,7 @@ void erts_lcnt_unlock(erts_lcnt_lock_t *lock) { erts_aint_t flowstate; #endif if (erts_lcnt_rt_options & ERTS_LCNT_OPT_SUSPEND) return; + if (!ERTS_LCNT_LOCK_TYPE(lock)) return; #ifdef DEBUG /* flowstate */ flowstate = ethr_atomic_read(&lock->flowstate); @@ -537,6 +554,7 @@ void erts_lcnt_unlock(erts_lcnt_lock_t *lock) { void erts_lcnt_trylock_opt(erts_lcnt_lock_t *lock, int res, Uint16 option) { if (erts_lcnt_rt_options & ERTS_LCNT_OPT_SUSPEND) return; + if (!ERTS_LCNT_LOCK_TYPE(lock)) return; /* Determine lock_state via res instead of state */ if (res != EBUSY) { if (option & ERTS_LCNT_LO_WRITE) ethr_atomic_inc(&lock->w_state); @@ -555,6 +573,7 @@ void erts_lcnt_trylock(erts_lcnt_lock_t *lock, int res) { erts_aint_t flowstate; #endif if (erts_lcnt_rt_options & ERTS_LCNT_OPT_SUSPEND) return; + if (!ERTS_LCNT_LOCK_TYPE(lock)) return; if (res != EBUSY) { #ifdef DEBUG diff --git a/erts/emulator/beam/erl_lock_count.h b/erts/emulator/beam/erl_lock_count.h index 6306580ae4..690551c71f 100644 --- a/erts/emulator/beam/erl_lock_count.h +++ b/erts/emulator/beam/erl_lock_count.h @@ -89,6 +89,7 @@ #define ERTS_LCNT_OPT_LOCATION (((Uint16) 1) << 1) #define ERTS_LCNT_OPT_PROCLOCK (((Uint16) 1) << 2) #define ERTS_LCNT_OPT_COPYSAVE (((Uint16) 1) << 3) +#define ERTS_LCNT_OPT_PORTLOCK (((Uint16) 1) << 4) typedef struct { unsigned long s; @@ -201,5 +202,7 @@ void erts_lcnt_clear_counters(void); char *erts_lcnt_lock_type(Uint16 type); erts_lcnt_data_t *erts_lcnt_get_data(void); +#define ERTS_LCNT_LOCK_TYPE(lockp) ((lockp)->flag & ERTS_LCNT_LT_ALL) + #endif /* ifdef ERTS_ENABLE_LOCK_COUNT */ #endif /* ifndef ERTS_LOCK_COUNT_H__ */ diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h index 2683b8540d..28aaedf2e2 100644 --- a/erts/emulator/beam/erl_process.h +++ b/erts/emulator/beam/erl_process.h @@ -1402,10 +1402,14 @@ ERTS_GLB_INLINE Eterm erts_get_current_pid(void); ERTS_GLB_INLINE Uint erts_get_scheduler_id(void); ERTS_GLB_INLINE ErtsRunQueue *erts_get_runq_proc(Process *p); ERTS_GLB_INLINE ErtsRunQueue *erts_get_runq_current(ErtsSchedulerData *esdp); +#ifndef ERTS_ENABLE_LOCK_COUNT ERTS_GLB_INLINE void erts_smp_runq_lock(ErtsRunQueue *rq); +#endif ERTS_GLB_INLINE int erts_smp_runq_trylock(ErtsRunQueue *rq); ERTS_GLB_INLINE void erts_smp_runq_unlock(ErtsRunQueue *rq); +#ifndef ERTS_ENABLE_LOCK_COUNT ERTS_GLB_INLINE void erts_smp_xrunq_lock(ErtsRunQueue *rq, ErtsRunQueue *xrq); +#endif ERTS_GLB_INLINE void erts_smp_xrunq_unlock(ErtsRunQueue *rq, ErtsRunQueue *xrq); ERTS_GLB_INLINE void erts_smp_runqs_lock(ErtsRunQueue *rq1, ErtsRunQueue *rq2); ERTS_GLB_INLINE void erts_smp_runqs_unlock(ErtsRunQueue *rq1, ErtsRunQueue *rq2); @@ -1494,6 +1498,12 @@ erts_get_runq_current(ErtsSchedulerData *esdp) #endif } +#ifdef ERTS_ENABLE_LOCK_COUNT + +#define erts_smp_runq_lock(rq) erts_smp_mtx_lock_x(&(rq)->mtx, __FILE__, __LINE__) + +#else + ERTS_GLB_INLINE void erts_smp_runq_lock(ErtsRunQueue *rq) { @@ -1502,6 +1512,8 @@ erts_smp_runq_lock(ErtsRunQueue *rq) #endif } +#endif + ERTS_GLB_INLINE int erts_smp_runq_trylock(ErtsRunQueue *rq) { @@ -1520,6 +1532,31 @@ erts_smp_runq_unlock(ErtsRunQueue *rq) #endif } +#ifdef ERTS_ENABLE_LOCK_COUNT + +#define erts_smp_xrunq_lock(rq, xrq) erts_smp_xrunq_lock_x((rq), (xrq), __FILE__, __LINE__) + +ERTS_GLB_INLINE void +erts_smp_xrunq_lock_x(ErtsRunQueue *rq, ErtsRunQueue *xrq, char* file, int line) +{ +#ifdef ERTS_SMP + ERTS_SMP_LC_ASSERT(erts_smp_lc_mtx_is_locked(&rq->mtx)); + if (xrq != rq) { + if (erts_smp_mtx_trylock(&xrq->mtx) == EBUSY) { + if (rq < xrq) + erts_smp_mtx_lock_x(&xrq->mtx, file, line); + else { + erts_smp_mtx_unlock(&rq->mtx); + erts_smp_mtx_lock_x(&xrq->mtx, file, line); + erts_smp_mtx_lock_x(&rq->mtx, file, line); + } + } + } +#endif +} + +#else + ERTS_GLB_INLINE void erts_smp_xrunq_lock(ErtsRunQueue *rq, ErtsRunQueue *xrq) { @@ -1539,6 +1576,8 @@ erts_smp_xrunq_lock(ErtsRunQueue *rq, ErtsRunQueue *xrq) #endif } +#endif + ERTS_GLB_INLINE void erts_smp_xrunq_unlock(ErtsRunQueue *rq, ErtsRunQueue *xrq) { diff --git a/erts/emulator/beam/erl_process_lock.c b/erts/emulator/beam/erl_process_lock.c index a5a753b798..b3b4601a31 100644 --- a/erts/emulator/beam/erl_process_lock.c +++ b/erts/emulator/beam/erl_process_lock.c @@ -1002,7 +1002,7 @@ erts_proc_lock_init(Process *p) #ifdef ERTS_ENABLE_LOCK_COUNT void erts_lcnt_proc_lock_init(Process *p) { - + if (erts_lcnt_rt_options & ERTS_LCNT_OPT_PROCLOCK) { if (p->id != ERTS_INVALID_PID) { erts_lcnt_init_lock_x(&(p->lock.lcnt_main), "proc_main", ERTS_LCNT_LT_PROCLOCK, p->id); erts_lcnt_init_lock_x(&(p->lock.lcnt_msgq), "proc_msgq", ERTS_LCNT_LT_PROCLOCK, p->id); @@ -1014,6 +1014,12 @@ void erts_lcnt_proc_lock_init(Process *p) { erts_lcnt_init_lock(&(p->lock.lcnt_link), "proc_link", ERTS_LCNT_LT_PROCLOCK); erts_lcnt_init_lock(&(p->lock.lcnt_status), "proc_status", ERTS_LCNT_LT_PROCLOCK); } + } else { + sys_memzero(&(p->lock.lcnt_main), sizeof(p->lock.lcnt_main)); + sys_memzero(&(p->lock.lcnt_msgq), sizeof(p->lock.lcnt_msgq)); + sys_memzero(&(p->lock.lcnt_link), sizeof(p->lock.lcnt_link)); + sys_memzero(&(p->lock.lcnt_status), sizeof(p->lock.lcnt_status)); + } } @@ -1108,6 +1114,26 @@ void erts_lcnt_proc_trylock(erts_proc_lock_t *lock, ErtsProcLocks locks, int res } } + +void erts_lcnt_enable_proc_lock_count(int enable) { + int i; + + for (i = 0; i < erts_max_processes; ++i) { + Process* p = process_tab[i]; + if (p) { + if (enable) { + if (!ERTS_LCNT_LOCK_TYPE(&(p->lock.lcnt_main))) { + erts_lcnt_proc_lock_init(p); + } + } else { + if (ERTS_LCNT_LOCK_TYPE(&(p->lock.lcnt_main))) { + erts_lcnt_proc_lock_destroy(p); + } + } + } + } +} + #endif /* ifdef ERTS_ENABLE_LOCK_COUNT */ diff --git a/erts/emulator/beam/erl_process_lock.h b/erts/emulator/beam/erl_process_lock.h index 8dbdaccc68..413c45480c 100644 --- a/erts/emulator/beam/erl_process_lock.h +++ b/erts/emulator/beam/erl_process_lock.h @@ -215,6 +215,8 @@ void erts_lcnt_proc_lock_unaquire(erts_proc_lock_t *lock, ErtsProcLocks locks); void erts_lcnt_proc_unlock(erts_proc_lock_t *lock, ErtsProcLocks locks); void erts_lcnt_proc_trylock(erts_proc_lock_t *lock, ErtsProcLocks locks, int res); +void erts_lcnt_enable_proc_lock_count(int enable); + #endif /* ERTS_ENABLE_LOCK_COUNT*/ diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index b000e2c5d4..894872dbc0 100644..100755 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -173,6 +173,7 @@ struct port { char *name; /* String used in the open */ erts_driver_t* drv_ptr; UWord drv_data; + SWord os_pid; /* Child process ID */ ErtsProcList *suspended; /* List of suspended processes. */ LineBuf *linebuf; /* Buffer to hold data not ready for process to get (line oriented I/O)*/ @@ -1187,6 +1188,10 @@ void erts_fire_port_monitor(Port *prt, Eterm ref); void erts_smp_xports_unlock(Port *); #endif +#if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_COUNT) +void erts_lcnt_enable_io_lock_count(int enable); +#endif + #if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_CHECK) int erts_lc_is_port_locked(Port *); #endif diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index 8a2a43bebd..eb8db73bae 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -440,6 +440,7 @@ setup_port(Port* prt, Eterm pid, erts_driver_t *driver, sys_strcpy(new_name, name); erts_smp_runq_lock(runq); erts_smp_port_state_lock(prt); + prt->os_pid = -1; prt->status = ERTS_PORT_SFLG_CONNECTED | xstatus; prt->snapshot = erts_smp_atomic32_read_nob(&erts_ports_snapshot); old_name = prt->name; @@ -625,7 +626,11 @@ erts_open_driver(erts_driver_t* driver, /* Pointer to driver. */ port->lock = erts_alloc(ERTS_ALC_T_PORT_LOCK, sizeof(erts_smp_mtx_t)); erts_smp_mtx_init_x(port->lock, +#ifdef ERTS_ENABLE_LOCK_COUNT + (erts_lcnt_rt_options & ERTS_LCNT_OPT_PORTLOCK) ? "port_lock" : NULL, +#else "port_lock", +#endif port->id); xstatus |= ERTS_PORT_SFLG_PORT_SPECIFIC_LOCK; } @@ -783,7 +788,13 @@ driver_create_port(ErlDrvPort creator_port_ix, /* Creating port */ creator_port->xports = xplp; port->lock = erts_alloc(ERTS_ALC_T_PORT_LOCK, sizeof(erts_smp_mtx_t)); - erts_smp_mtx_init_locked_x(port->lock, "port_lock", port_id); + erts_smp_mtx_init_locked_x(port->lock, +#ifdef ERTS_ENABLE_LOCK_COUNT + (erts_lcnt_rt_options & ERTS_LCNT_OPT_PORTLOCK) ? "port_lock" : NULL, +#else + "port_lock", +#endif + port_id); xstatus |= ERTS_PORT_SFLG_PORT_SPECIFIC_LOCK; } @@ -1347,7 +1358,13 @@ void init_io(void) erts_smp_atomic_init_nob(&erts_port[i].refc, 0); erts_port[i].lock = NULL; erts_port[i].xports = NULL; - erts_smp_spinlock_init_x(&erts_port[i].state_lck, "port_state", make_small(i)); + erts_smp_spinlock_init_x(&erts_port[i].state_lck, +#ifdef ERTS_ENABLE_LOCK_COUNT + (erts_lcnt_rt_options & ERTS_LCNT_OPT_PORTLOCK) ? "port_state" : NULL, +#else + "port_state", +#endif + make_small(0)); #endif erts_port[i].tracer_proc = NIL; erts_port[i].trace_flags = 0; @@ -1380,6 +1397,27 @@ void init_io(void) erts_smp_mtx_unlock(&erts_driver_list_lock); } +#if defined(ERTS_ENABLE_LOCK_COUNT) && defined(ERTS_SMP) +void erts_lcnt_enable_io_lock_count(int enable) { + int i; + + for (i = 0; i < erts_max_ports; i++) { + Port* p = &erts_port[i]; + if (enable) { + erts_lcnt_init_lock_x(&p->state_lck.lcnt, "port_state", ERTS_LCNT_LT_SPINLOCK, make_small(i)); + if (p->lock) { + erts_lcnt_init_lock_x(&p->lock->lcnt, "port_lock", ERTS_LCNT_LT_MUTEX, make_small(i)); + } + } else { + erts_lcnt_destroy_lock(&p->state_lck.lcnt); + if (p->lock) { + erts_lcnt_destroy_lock(&p->lock->lcnt); + } + } + } +} +#endif + /* * Buffering of data when using line oriented I/O on ports */ diff --git a/erts/emulator/sys/unix/sys.c b/erts/emulator/sys/unix/sys.c index f94e0f2296..bf69f3bf90 100644 --- a/erts/emulator/sys/unix/sys.c +++ b/erts/emulator/sys/unix/sys.c @@ -1163,6 +1163,8 @@ static int set_driver_data(int port_num, report_exit_list = report_exit; } + erts_port[port_num].os_pid = pid; + if (read_write & DO_READ) { driver_data[ifd].packet_bytes = packet_bytes; driver_data[ifd].port_num = port_num; diff --git a/erts/emulator/sys/win32/sys.c b/erts/emulator/sys/win32/sys.c index b106f0932d..acbbfc2ce9 100755 --- a/erts/emulator/sys/win32/sys.c +++ b/erts/emulator/sys/win32/sys.c @@ -68,9 +68,9 @@ static int async_write_file(struct async_io* aio, LPVOID buf, DWORD numToWrite); static int get_overlapped_result(struct async_io* aio, LPDWORD pBytesRead, BOOL wait); static BOOL create_child_process(char *, HANDLE, HANDLE, - HANDLE, LPHANDLE, BOOL, - LPVOID, LPTSTR, unsigned, - char **, int *); + HANDLE, LPHANDLE, LPDWORD, BOOL, + LPVOID, LPTSTR, unsigned, + char **, int *); static int create_pipe(LPHANDLE, LPHANDLE, BOOL, BOOL); static int application_type(const char* originalName, char fullPath[MAX_PATH], BOOL search_in_path, BOOL handle_quotes, @@ -1136,6 +1136,7 @@ spawn_start(ErlDrvPort port_num, char* name, SysDriverOpts* opts) HANDLE hChildStdin = INVALID_HANDLE_VALUE; /* Child's stdin. */ HANDLE hChildStdout = INVALID_HANDLE_VALUE; /* Child's stout. */ HANDLE hChildStderr = INVALID_HANDLE_VALUE; /* Child's sterr. */ + DWORD pid; int close_child_stderr = 0; DriverData* dp; /* Pointer to driver data. */ ErlDrvData retval = ERL_DRV_ERROR_GENERAL; /* Return value. */ @@ -1211,6 +1212,7 @@ spawn_start(ErlDrvPort port_num, char* name, SysDriverOpts* opts) hChildStdout, hChildStderr, &dp->port_pid, + &pid, opts->hide_window, (LPVOID) envir, (LPTSTR) opts->wd, @@ -1254,6 +1256,9 @@ spawn_start(ErlDrvPort port_num, char* name, SysDriverOpts* opts) #endif retval = set_driver_data(dp, hFromChild, hToChild, opts->read_write, opts->exit_status); + if (retval != ERL_DRV_ERROR_GENERAL && retval != ERL_DRV_ERROR_ERRNO) + /* We assume that this cannot generate a negative number */ + erts_port[port_num].os_pid = (SWord) pid; } if (retval != ERL_DRV_ERROR_GENERAL && retval != ERL_DRV_ERROR_ERRNO) @@ -1397,7 +1402,8 @@ create_child_process HANDLE hStdin, /* The standard input handle for child. */ HANDLE hStdout, /* The standard output handle for child. */ HANDLE hStderr, /* The standard error handle for child. */ - LPHANDLE phPid, /* Pointer to variable to received PID. */ + LPHANDLE phPid, /* Pointer to variable to received Process handle. */ + LPDWORD pdwID, /* Pointer to variable to received Process ID */ BOOL hide, /* Hide the window unconditionally. */ LPVOID env, /* Environment for the child */ LPTSTR wd, /* Working dir for the child */ @@ -1629,7 +1635,8 @@ create_child_process } CloseHandle(piProcInfo.hThread); /* Necessary to avoid resource leak. */ *phPid = piProcInfo.hProcess; - + *pdwID = piProcInfo.dwProcessId; + if (applType == APPL_DOS) { WaitForSingleObject(hProcess, 50); } diff --git a/erts/emulator/test/port_bif_SUITE.erl b/erts/emulator/test/port_bif_SUITE.erl index d9c82aba0e..8feea87d7e 100644 --- a/erts/emulator/test/port_bif_SUITE.erl +++ b/erts/emulator/test/port_bif_SUITE.erl @@ -24,6 +24,7 @@ init_per_group/2,end_per_group/2, command/1, command_e_1/1, command_e_2/1, command_e_3/1, command_e_4/1, port_info1/1, port_info2/1, + port_info_os_pid/1, connect/1, control/1, echo_to_busy/1]). -export([do_command_e_1/1, do_command_e_2/1, do_command_e_4/1]). @@ -41,7 +42,7 @@ all() -> groups() -> [{command_e, [], [command_e_1, command_e_2, command_e_3, command_e_4]}, - {port_info, [], [port_info1, port_info2]}]. + {port_info, [], [port_info1, port_info2, port_info_os_pid]}]. init_per_suite(Config) -> Config. @@ -65,15 +66,15 @@ end_per_testcase(_Func, Config) when is_list(Config) -> test_server:timetrap_cancel(Dog). command(Config) when is_list(Config) -> - ?line load_control_drv(Config), - - ?line P = open_port({spawn, control_drv}, []), - ?line do_command(P, "hello"), - ?line do_command(P, <<"hello">>), - ?line do_command(P, sub_bin(<<"1234kalle">>)), - ?line do_command(P, unaligned_sub_bin(<<"blurf">>)), - ?line do_command(P, ["bl"|unaligned_sub_bin(<<"urf">>)]), - ?line true = erlang:port_close(P), + load_control_drv(Config), + + P = open_port({spawn, control_drv}, []), + do_command(P, "hello"), + do_command(P, <<"hello">>), + do_command(P, sub_bin(<<"1234kalle">>)), + do_command(P, unaligned_sub_bin(<<"blurf">>)), + do_command(P, ["bl"|unaligned_sub_bin(<<"urf">>)]), + true = erlang:port_close(P), ok. do_command(P, Data) -> @@ -94,139 +95,163 @@ do_command(P, Data) -> %% port_command/2: badarg 1st arg command_e_1(Config) when is_list(Config) -> - ?line DataDir = ?config(data_dir, Config), - ?line Program = filename:join(DataDir, "port_test"), + DataDir = ?config(data_dir, Config), + Program = filename:join(DataDir, "port_test"), process_flag(trap_exit, true), - ?line _ = spawn_link(?MODULE, do_command_e_1, [Program]), - ?line receive - {'EXIT', Pid, {badarg, _}} when is_pid(Pid) -> - ok; - Other -> - ?line test_server:fail(Other) - after 10000 -> - ?line test_server:fail(timeout) - end, + _ = spawn_link(?MODULE, do_command_e_1, [Program]), + receive + {'EXIT', Pid, {badarg, _}} when is_pid(Pid) -> + ok; + Other -> + test_server:fail(Other) + after 10000 -> + test_server:fail(timeout) + end, ok. do_command_e_1(Program) -> - ?line _ = open_port({spawn, Program}, []), - ?line erlang:port_command(apple, "plock"), + _ = open_port({spawn, Program}, []), + erlang:port_command(apple, "plock"), exit(survived). %% port_command/2: badarg 2nd arg command_e_2(Config) when is_list(Config) -> - ?line DataDir = ?config(data_dir, Config), - ?line Program = filename:join(DataDir, "port_test"), + DataDir = ?config(data_dir, Config), + Program = filename:join(DataDir, "port_test"), process_flag(trap_exit, true), - ?line _ = spawn_link(?MODULE, do_command_e_2, [Program]), - ?line receive - {'EXIT', Pid, {badarg, _}} when is_pid(Pid) -> - ok; - Other -> - ?line test_server:fail(Other) - after 10000 -> - ?line test_server:fail(timeout) - end, + _ = spawn_link(?MODULE, do_command_e_2, [Program]), + receive + {'EXIT', Pid, {badarg, _}} when is_pid(Pid) -> + ok; + Other -> + test_server:fail(Other) + after 10000 -> + test_server:fail(timeout) + end, ok. do_command_e_2(Program) -> - ?line P = open_port({spawn, Program}, []), - ?line erlang:port_command(P, 1), + P = open_port({spawn, Program}, []), + erlang:port_command(P, 1), exit(survived). %% port_command/2: Posix signals trapped command_e_3(Config) when is_list(Config) -> - ?line DataDir = ?config(data_dir, Config), - ?line Program = filename:join(DataDir, "port_test"), + DataDir = ?config(data_dir, Config), + Program = filename:join(DataDir, "port_test"), process_flag(trap_exit, true), - ?line P = open_port({spawn, Program}, [{packet, 1}]), - ?line Data = lists:duplicate(257, $a), - ?line erlang:port_command(P, Data), - ?line receive - {'EXIT', Port, einval} when is_port(Port) -> - ok; - Other -> - test_server:fail(Other) - after 10000 -> - test_server:fail(timeout) - end, + P = open_port({spawn, Program}, [{packet, 1}]), + Data = lists:duplicate(257, $a), + erlang:port_command(P, Data), + receive + {'EXIT', Port, einval} when is_port(Port) -> + ok; + Other -> + test_server:fail(Other) + after 10000 -> + test_server:fail(timeout) + end, ok. %% port_command/2: Posix exit signals not trapped command_e_4(Config) when is_list(Config) -> - ?line DataDir = ?config(data_dir, Config), - ?line Program = filename:join(DataDir, "port_test"), + DataDir = ?config(data_dir, Config), + Program = filename:join(DataDir, "port_test"), process_flag(trap_exit, true), - ?line _ = spawn_link(?MODULE, do_command_e_4, [Program]), - ?line receive - {'EXIT', Pid, {einval, _}} when is_pid(Pid) -> - ok; - Other -> - ?line test_server:fail(Other) - after 10000 -> - ?line test_server:fail(timeout) - end, + _ = spawn_link(?MODULE, do_command_e_4, [Program]), + receive + {'EXIT', Pid, {einval, _}} when is_pid(Pid) -> + ok; + Other -> + test_server:fail(Other) + after 10000 -> + test_server:fail(timeout) + end, ok. do_command_e_4(Program) -> - ?line P = open_port({spawn, Program}, [{packet, 1}]), - ?line Data = lists:duplicate(257, $a), - ?line erlang:port_command(P, Data), + P = open_port({spawn, Program}, [{packet, 1}]), + Data = lists:duplicate(257, $a), + erlang:port_command(P, Data), exit(survived). %% Tests the port_info/1 BIF port_info1(Config) when is_list(Config) -> - ?line load_control_drv(Config), + load_control_drv(Config), Me=self(), - ?line P = open_port({spawn, control_drv}, []), - ?line A1 = erlang:port_info(P), - ?line false = lists:keysearch(registered_name, 1, A1), - ?line register(myport, P), - ?line A = erlang:port_info(P), - ?line {value,{registered_name,myport}}= - lists:keysearch(registered_name, 1, A), - ?line {value,{name,"control_drv"}}=lists:keysearch(name, 1, A), - ?line {value,{links,[Me]}}=lists:keysearch(links, 1, A), - ?line {value,{id,_IdNum}}=lists:keysearch(id, 1, A), - ?line {value,{connected,_}}=lists:keysearch(connected, 1, A), - ?line {value,{input,0}}=lists:keysearch(input, 1, A), - ?line {value,{output,0}}=lists:keysearch(output, 1, A), - ?line true=erlang:port_close(P), + P = open_port({spawn, control_drv}, []), + A1 = erlang:port_info(P), + false = lists:keysearch(registered_name, 1, A1), + register(myport, P), + A = erlang:port_info(P), + {value,{registered_name,myport}}= lists:keysearch(registered_name, 1, A), + {value,{name,"control_drv"}}=lists:keysearch(name, 1, A), + {value,{links,[Me]}}=lists:keysearch(links, 1, A), + {value,{id,_IdNum}}=lists:keysearch(id, 1, A), + {value,{connected,_}}=lists:keysearch(connected, 1, A), + {value,{input,0}}=lists:keysearch(input, 1, A), + {value,{output,0}}=lists:keysearch(output, 1, A), + {value,{os_pid,undefined}}=lists:keysearch(os_pid, 1, A), % linked-in driver doesn't have a OS pid + true=erlang:port_close(P), ok. %% Tests erlang:port_info/2" port_info2(Config) when is_list(Config) -> - ?line load_control_drv(Config), + load_control_drv(Config), - ?line P = open_port({spawn,control_drv}, [binary]), - ?line [] = erlang:port_info(P, registered_name), - ?line register(myport, P), - ?line {registered_name, myport} = erlang:port_info(P, registered_name), + P = open_port({spawn,control_drv}, [binary]), + [] = erlang:port_info(P, registered_name), + register(myport, P), + {registered_name, myport} = erlang:port_info(P, registered_name), - ?line {name, "control_drv"}=erlang:port_info(P, name), - ?line {id, _IdNum} = erlang:port_info(P, id), + {name, "control_drv"}=erlang:port_info(P, name), + {id, _IdNum} = erlang:port_info(P, id), Me=self(), - ?line {links, [Me]} = erlang:port_info(P, links), - ?line {connected, Me} = erlang:port_info(P, connected), - ?line {input, 0}=erlang:port_info(P, input), - ?line {output,0}=erlang:port_info(P, output), - - ?line erlang:port_control(P, $i, "abc"), - ?line receive - {P,{data,<<"abc">>}} -> ok - end, - ?line {input,3} = erlang:port_info(P, input), - ?line {output,0} = erlang:port_info(P, output), - - ?line Bin = list_to_binary(lists:duplicate(2047, 42)), - ?line output_test(P, Bin, 3, 0), + {links, [Me]} = erlang:port_info(P, links), + {connected, Me} = erlang:port_info(P, connected), + {input, 0}=erlang:port_info(P, input), + {output,0}=erlang:port_info(P, output), + {os_pid, undefined}=erlang:port_info(P, os_pid), % linked-in driver doesn't have a OS pid + + erlang:port_control(P, $i, "abc"), + receive + {P,{data,<<"abc">>}} -> ok + end, + {input,3} = erlang:port_info(P, input), + {output,0} = erlang:port_info(P, output), + + Bin = list_to_binary(lists:duplicate(2047, 42)), + output_test(P, Bin, 3, 0), - ?line true = erlang:port_close(P), + true = erlang:port_close(P), + ok. + +%% Tests the port_info/1,2 os_pid option BIF +port_info_os_pid(Config) when is_list(Config) -> + case os:type() of + {unix,_} -> + do_port_info_os_pid(); + _ -> + {skip,"Only on Unix."} + end. + +do_port_info_os_pid() -> + P = open_port({spawn, "echo $$"}, [eof]), + A = erlang:port_info(P), + {os_pid, InfoOSPid} = erlang:port_info(P, os_pid), + EchoPidStr = receive + {P, {data, EchoPidStr0}} -> EchoPidStr0 + after 10000 -> test_server:fail(timeout) + end, + {ok, [EchoPid], []} = io_lib:fread("~u\n", EchoPidStr), + {value,{os_pid, InfoOSPid}}=lists:keysearch(os_pid, 1, A), + EchoPid = InfoOSPid, + true = erlang:port_close(P), ok. output_test(_, _, Input, Output) when Output > 16#1fffffff -> @@ -237,7 +262,7 @@ output_test(P, Bin, Input0, Output0) -> {P,{data,Bin}} -> ok; Other -> io:format("~p", [Other]), - ?line ?t:fail() + ?t:fail() end, Input = Input0 + size(Bin), Output = Output0 + size(Bin), @@ -254,109 +279,106 @@ output_test(P, Bin, Input0, Output0) -> %% Tests the port_connect/2 BIF. connect(Config) when is_list(Config) -> - ?line load_control_drv(Config), + load_control_drv(Config), - ?line P = open_port({spawn, control_drv}, []), + P = open_port({spawn, control_drv}, []), register(myport, P), - ?line true = erlang:port_connect(myport, self()), + true = erlang:port_connect(myport, self()), %% Connect the port to another process. Data = "hello, world", Parent = self(), - ?line Rec = - fun(Me) -> receive - {P,{data,Data}} -> - Parent ! connect_ok, - Me(Me) - end - end, - ?line RecPid = spawn_link(fun() -> Rec(Rec) end), - ?line true = erlang:port_connect(P, RecPid), - ?line unlink(P), + Rec = fun(Me) -> + receive + {P,{data,Data}} -> + Parent ! connect_ok, + Me(Me) + end + end, + RecPid = spawn_link(fun() -> Rec(Rec) end), + true = erlang:port_connect(P, RecPid), + unlink(P), %% Send a command to the port and make sure that the %% other process receives the echo. - ?line erlang:port_command(P, Data), - ?line receive - connect_ok -> ok - end, + erlang:port_command(P, Data), + receive + connect_ok -> ok + end, %% Tests some errors. - ?line {'EXIT',{badarg, _}}=(catch erlang:port_connect(self(), self())), - ?line {'EXIT',{badarg, _}}=(catch erlang:port_connect(self(), P)), - ?line {'EXIT',{badarg, _}}=(catch erlang:port_connect(P, P)), - ?line {'EXIT',{badarg, _}}=(catch erlang:port_connect(P, xxxx)), - ?line {'EXIT',{badarg, _}}=(catch erlang:port_connect(P, [])), + {'EXIT',{badarg, _}}=(catch erlang:port_connect(self(), self())), + {'EXIT',{badarg, _}}=(catch erlang:port_connect(self(), P)), + {'EXIT',{badarg, _}}=(catch erlang:port_connect(P, P)), + {'EXIT',{badarg, _}}=(catch erlang:port_connect(P, xxxx)), + {'EXIT',{badarg, _}}=(catch erlang:port_connect(P, [])), - ?line process_flag(trap_exit, true), - ?line exit(P, you_should_die), - ?line receive - {'EXIT',RecPid,you_should_die} -> ok; - Other -> ?line ?t:fail({bad_message,Other}) - end, + process_flag(trap_exit, true), + exit(P, you_should_die), + receive + {'EXIT',RecPid,you_should_die} -> ok; + Other -> ?line ?t:fail({bad_message,Other}) + end, %% Done. ok. %% Tests port_control/3 control(Config) when is_list(Config) -> - ?line load_control_drv(Config), - ?line P = open_port({spawn, control_drv}, []), + load_control_drv(Config), + P = open_port({spawn, control_drv}, []), %% Test invalid (out-of-range) arguments. - ?line {'EXIT', {badarg, _}} = (catch erlang:port_control(self(), 1, [])), + {'EXIT', {badarg, _}} = (catch erlang:port_control(self(), 1, [])), - ?line {'EXIT', {badarg, _}} = (catch erlang:port_control(P, -1, [])), - ?line {'EXIT', {badarg, _}} = (catch erlang:port_control(P, -34887348739733833, [])), - ?line {'EXIT', {badarg, _}} = (catch erlang:port_control(P, 16#100000000, [])), - ?line {'EXIT', {badarg, _}} = (catch erlang:port_control(P, a, [])), - ?line {'EXIT', {badarg, _}} = (catch erlang:port_control(P, 'e', dum)), - ?line {'EXIT', {badarg, _}} = (catch erlang:port_control(P, $e, dum)), - ?line {'EXIT', {badarg, _}} = (catch erlang:port_control(P, $e, fun(X) -> X end)), - ?line {'EXIT', {badarg, _}} = (catch erlang:port_control(P, $e, - [fun(X) -> X end])), - ?line {'EXIT', {badarg, _}} = (catch erlang:port_control(P, $e, - [1|fun(X) -> X end])), + {'EXIT', {badarg, _}} = (catch erlang:port_control(P, -1, [])), + {'EXIT', {badarg, _}} = (catch erlang:port_control(P, -34887348739733833, [])), + {'EXIT', {badarg, _}} = (catch erlang:port_control(P, 16#100000000, [])), + {'EXIT', {badarg, _}} = (catch erlang:port_control(P, a, [])), + {'EXIT', {badarg, _}} = (catch erlang:port_control(P, 'e', dum)), + {'EXIT', {badarg, _}} = (catch erlang:port_control(P, $e, dum)), + {'EXIT', {badarg, _}} = (catch erlang:port_control(P, $e, fun(X) -> X end)), + {'EXIT', {badarg, _}} = (catch erlang:port_control(P, $e, [fun(X) -> X end])), + {'EXIT', {badarg, _}} = (catch erlang:port_control(P, $e, [1|fun(X) -> X end])), %% Test errors detected by the driver. - ?line {'EXIT', {badarg, _}} = (catch erlang:port_control(P, 177, [])), - ?line {'EXIT', {badarg, _}} = (catch erlang:port_control(P, 155, - random_packet(1024))), + {'EXIT', {badarg, _}} = (catch erlang:port_control(P, 177, [])), + {'EXIT', {badarg, _}} = (catch erlang:port_control(P, 155, random_packet(1024))), %% Test big op codes. register(myport, P), - ?line test_op(myport, 256), - ?line test_op(P, 256), - ?line test_op(P, 16#0033A837), - ?line test_op(P, 16#0ab37938), - ?line test_op(P, 16#eab37938), - ?line test_op(P, 16#ffffFFFF), + test_op(myport, 256), + test_op(P, 256), + test_op(P, 16#0033A837), + test_op(P, 16#0ab37938), + test_op(P, 16#eab37938), + test_op(P, 16#ffffFFFF), %% Test the echo function of the driver. - ?line echo(P, 0), - ?line echo(P, 1), - ?line echo(P, 10), - ?line echo(P, 13), - ?line echo(P, 63), - ?line echo(P, 64), - ?line echo(P, 65), - ?line echo(P, 127), - ?line echo(P, 1023), - ?line echo(P, 1024), - ?line echo(P, 11243), - ?line echo(P, 70000), + echo(P, 0), + echo(P, 1), + echo(P, 10), + echo(P, 13), + echo(P, 63), + echo(P, 64), + echo(P, 65), + echo(P, 127), + echo(P, 1023), + echo(P, 1024), + echo(P, 11243), + echo(P, 70000), %% Done. - ?line true=erlang:port_close(myport), + true = erlang:port_close(myport), ok. test_op(P, Op) -> @@ -364,23 +386,23 @@ test_op(P, Op) -> <<Op:32>> = list_to_binary(R). echo_to_busy(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(10)), - ?line load_control_drv(Config), - ?line P = open_port({spawn, control_drv}, []), - ?line erlang:port_control(P, $b, [1]), % Set to busy. + Dog = test_server:timetrap(test_server:seconds(10)), + load_control_drv(Config), + P = open_port({spawn, control_drv}, []), + erlang:port_control(P, $b, [1]), % Set to busy. Self = self(), - ?line Echoer = spawn(fun() -> echoer(P, Self) end), - ?line receive after 500 -> ok end, - ?line erlang:port_control(P, $b, [0]), % Set to not busy. - ?line receive - {Echoer, done} -> - ok; - {Echoer, Other} -> - test_server:fail(Other); - Other -> - test_server:fail({unexpected_message, Other}) - end, - ?line test_server:timetrap_cancel(Dog), + Echoer = spawn(fun() -> echoer(P, Self) end), + receive after 500 -> ok end, + erlang:port_control(P, $b, [0]), % Set to not busy. + receive + {Echoer, done} -> + ok; + {Echoer, Other} -> + test_server:fail(Other); + Other -> + test_server:fail({unexpected_message, Other}) + end, + test_server:timetrap_cancel(Dog), ok. echoer(P, ReplyTo) -> @@ -405,9 +427,9 @@ echo(P, Size) -> Packet = erlang:port_control(P, $e, [unaligned_sub_bin(Bin)]). load_control_drv(Config) when is_list(Config) -> - ?line DataDir = ?config(data_dir, Config), - ?line erl_ddll:start(), - ?line ok = load_driver(DataDir, "control_drv"). + DataDir = ?config(data_dir, Config), + erl_ddll:start(), + ok = load_driver(DataDir, "control_drv"). load_driver(Dir, Driver) -> case erl_ddll:load_driver(Dir, Driver) of @@ -459,4 +481,3 @@ sub_bin(Bin) when is_binary(Bin) -> B. id(I) -> I. - |