aboutsummaryrefslogtreecommitdiffstats
path: root/erts/emulator/sys
diff options
context:
space:
mode:
Diffstat (limited to 'erts/emulator/sys')
-rw-r--r--erts/emulator/sys/unix/sys.c82
-rwxr-xr-xerts/emulator/sys/win32/sys.c96
2 files changed, 149 insertions, 29 deletions
diff --git a/erts/emulator/sys/unix/sys.c b/erts/emulator/sys/unix/sys.c
index c1fa00b4ea..c485a4eece 100644
--- a/erts/emulator/sys/unix/sys.c
+++ b/erts/emulator/sys/unix/sys.c
@@ -58,7 +58,6 @@
#define __DARWIN__ 1
#endif
-
#ifdef USE_THREADS
#include "erl_threads.h"
#endif
@@ -71,7 +70,6 @@ static erts_smp_rwmtx_t environ_rwmtx;
#define MAX_VSIZE 16 /* Max number of entries allowed in an I/O
* vector sock_sendv().
*/
-
/*
* Don't need global.h, but bif_table.h (included by bif.h),
* won't compile otherwise
@@ -123,6 +121,15 @@ struct ErtsSysReportExit_ {
#endif
};
+/* This data is shared by these drivers - initialized by spawn_init() */
+static struct driver_data {
+ int port_num, ofd, packet_bytes;
+ ErtsSysReportExit *report_exit;
+ int pid;
+ int alive;
+ int status;
+} *driver_data; /* indexed by fd */
+
static ErtsSysReportExit *report_exit_list;
#if CHLDWTHR && !defined(ERTS_SMP)
static ErtsSysReportExit *report_exit_transit_list;
@@ -680,17 +687,40 @@ static RETSIGTYPE break_handler(int sig)
#endif /* 0 */
static ERTS_INLINE void
-prepare_crash_dump(void)
+prepare_crash_dump(int secs)
{
+#define NUFBUF (3)
int i, max;
char env[21]; /* enough to hold any 64-bit integer */
size_t envsz;
+ DeclareTmpHeapNoproc(heap,NUFBUF);
+ Port *heart_port;
+ Eterm *hp = heap;
+ Eterm list = NIL;
+ int heart_fd[2] = {-1,-1};
+
+ UseTmpHeapNoproc(NUFBUF);
if (ERTS_PREPARED_CRASH_DUMP)
return; /* We have already been called */
+ heart_port = erts_get_heart_port();
+ if (heart_port) {
+ /* hearts input fd
+ * We "know" drv_data is the in_fd since the port is started with read|write
+ */
+ heart_fd[0] = (int)heart_port->drv_data;
+ heart_fd[1] = (int)driver_data[heart_fd[0]].ofd;
+
+ list = CONS(hp, make_small(8), list); hp += 2;
+
+ /* send to heart port, CMD = 8, i.e. prepare crash dump =o */
+ erts_write_to_port(ERTS_INVALID_PID, heart_port, list);
+ }
+
/* Make sure we unregister at epmd (unknown fd) and get at least
one free filedescriptor (for erl_crash.dump) */
+
max = max_files;
if (max < 1024)
max = 1024;
@@ -704,11 +734,15 @@ prepare_crash_dump(void)
if (i == async_fd[0] || i == async_fd[1])
continue;
#endif
+ /* We don't want to close our heart yet ... */
+ if (i == heart_fd[0] || i == heart_fd[1])
+ continue;
+
close(i);
}
envsz = sizeof(env);
- i = erts_sys_getenv_raw("ERL_CRASH_DUMP_NICE", env, &envsz);
+ i = erts_sys_getenv__("ERL_CRASH_DUMP_NICE", env, &envsz);
if (i >= 0) {
int nice_val;
nice_val = i != 0 ? 0 : atoi(env);
@@ -717,21 +751,21 @@ prepare_crash_dump(void)
}
erts_silence_warn_unused_result(nice(nice_val));
}
-
- envsz = sizeof(env);
- i = erts_sys_getenv_raw("ERL_CRASH_DUMP_SECONDS", env, &envsz);
- if (i >= 0) {
- unsigned sec;
- sec = (unsigned) i != 0 ? 0 : atoi(env);
- alarm(sec);
- }
+ /* Positive secs means an alarm must be set
+ * 0 or negative means no alarm
+ */
+ if (secs > 0) {
+ alarm((unsigned int)secs);
+ }
+ UnUseTmpHeapNoproc(NUFBUF);
+#undef NUFBUF
}
void
-erts_sys_prepare_crash_dump(void)
+erts_sys_prepare_crash_dump(int secs)
{
- prepare_crash_dump();
+ prepare_crash_dump(secs);
}
static ERTS_INLINE void
@@ -773,7 +807,7 @@ sigusr1_exit(void)
is hung somewhere, so it won't be able to poll any flag we set here.
*/
ERTS_SET_GOT_SIGUSR1;
- prepare_crash_dump();
+ prepare_crash_dump((int)0);
erl_exit(1, "Received SIGUSR1\n");
}
@@ -1021,15 +1055,6 @@ void fini_getenv_state(GETENV_STATE *state)
#define ERTS_SYS_READ_BUF_SZ (64*1024)
-/* This data is shared by these drivers - initialized by spawn_init() */
-static struct driver_data {
- int port_num, ofd, packet_bytes;
- ErtsSysReportExit *report_exit;
- int pid;
- int alive;
- int status;
-} *driver_data; /* indexed by fd */
-
/* Driver interfaces */
static ErlDrvData spawn_start(ErlDrvPort, char*, SysDriverOpts*);
static ErlDrvData fd_start(ErlDrvPort, char*, SysDriverOpts*);
@@ -2417,6 +2442,15 @@ erts_sys_getenv_raw(char *key, char *value, size_t *size) {
return erts_sys_getenv(key, value, size);
}
+/*
+ * erts_sys_getenv
+ * returns:
+ * -1, if environment key is not set with a value
+ * 0, if environment key is set and value fits into buffer size
+ * 1, if environment key is set but does not fit into buffer size
+ * size is set with the needed buffer size value
+ */
+
int
erts_sys_getenv(char *key, char *value, size_t *size)
{
diff --git a/erts/emulator/sys/win32/sys.c b/erts/emulator/sys/win32/sys.c
index 6894d682e7..50f6219374 100755
--- a/erts/emulator/sys/win32/sys.c
+++ b/erts/emulator/sys/win32/sys.c
@@ -256,10 +256,25 @@ void erl_sys_args(int* argc, char** argv)
}
void
-erts_sys_prepare_crash_dump(void)
+erts_sys_prepare_crash_dump(int secs)
{
+ Port *heart_port;
+ Eterm heap[3];
+ Eterm *hp = heap;
+ Eterm list = NIL;
+
+ heart_port = erts_get_heart_port();
+
+ if (heart_port) {
+
+ list = CONS(hp, make_small(8), list); hp += 2;
+
+ /* send to heart port, CMD = 8, i.e. prepare crash dump =o */
+ erts_write_to_port(NIL, heart_port, list);
+ }
+
/* Windows - free file descriptors are hopefully available */
- return;
+ /* Alarm not used on windows */
}
static void
@@ -420,6 +435,8 @@ typedef struct async_io {
HANDLE ioAllowed; /* The thread will wait for this event
* before starting a new read or write.
*/
+ HANDLE flushEvent; /* Used to signal that a flush should be done. */
+ HANDLE flushReplyEvent; /* Used to signal that a flush has been done. */
DWORD pendingError; /* Used to delay presentating an error to Erlang
* until the check_io function is entered.
*/
@@ -878,6 +895,8 @@ init_async_io(AsyncIo* aio, int use_threads)
aio->ov.Offset = 0L;
aio->ov.OffsetHigh = 0L;
aio->ioAllowed = NULL;
+ aio->flushEvent = NULL;
+ aio->flushReplyEvent = NULL;
aio->pendingError = 0;
aio->bytesTransferred = 0;
#ifdef ERTS_SMP
@@ -890,6 +909,12 @@ init_async_io(AsyncIo* aio, int use_threads)
aio->ioAllowed = CreateAutoEvent(FALSE);
if (aio->ioAllowed == NULL)
return -1;
+ aio->flushEvent = CreateAutoEvent(FALSE);
+ if (aio->flushEvent == NULL)
+ return -1;
+ aio->flushReplyEvent = CreateAutoEvent(FALSE);
+ if (aio->flushReplyEvent == NULL)
+ return -1;
}
return 0;
}
@@ -923,6 +948,14 @@ release_async_io(AsyncIo* aio, ErlDrvPort port_num)
if (aio->ioAllowed != NULL)
CloseHandle(aio->ioAllowed);
aio->ioAllowed = NULL;
+
+ if (aio->flushEvent != NULL)
+ CloseHandle(aio->flushEvent);
+ aio->flushEvent = NULL;
+
+ if (aio->flushReplyEvent != NULL)
+ CloseHandle(aio->flushReplyEvent);
+ aio->flushReplyEvent = NULL;
}
/* ----------------------------------------------------------------------
@@ -2083,16 +2116,26 @@ threaded_writer(LPVOID param)
AsyncIo* aio = (AsyncIo *) param;
HANDLE thread = GetCurrentThread();
char* buf;
- DWORD numToWrite;
+ DWORD numToWrite, handle;
int ok;
+ HANDLE handles[2];
+ handles[0] = aio->ioAllowed;
+ handles[1] = aio->flushEvent;
for (;;) {
- WaitForSingleObject(aio->ioAllowed, INFINITE);
+ handle = WaitForMultipleObjects(2, handles, FALSE, INFINITE);
if (aio->flags & DF_EXIT_THREAD)
break;
+
buf = OV_BUFFER_PTR(aio);
numToWrite = OV_NUM_TO_READ(aio);
aio->pendingError = 0;
+
+ if (handle == (WAIT_OBJECT_0 + 1) && numToWrite == 0) {
+ SetEvent(aio->flushReplyEvent);
+ continue;
+ }
+
ok = WriteFile(aio->fd, buf, numToWrite, &aio->bytesTransferred, NULL);
if (!ok) {
aio->pendingError = GetLastError();
@@ -2127,7 +2170,11 @@ threaded_writer(LPVOID param)
}
}
}
- SetEvent(aio->ov.hEvent);
+ OV_NUM_TO_READ(aio) = 0;
+ if (handle == (WAIT_OBJECT_0 + 1))
+ SetEvent(aio->flushReplyEvent);
+ else
+ SetEvent(aio->ov.hEvent);
if (aio->pendingError != NO_ERROR || aio->bytesTransferred == 0)
break;
if (aio->flags & DF_EXIT_THREAD)
@@ -2193,6 +2240,43 @@ fd_start(ErlDrvPort port_num, char* name, SysDriverOpts* opts)
if ((dp = new_driver_data(port_num, opts->packet_bytes, 2, TRUE)) == NULL)
return ERL_DRV_ERROR_GENERAL;
+ /**
+ * Here is a brief description about how the fd driver works on windows.
+ *
+ * fd_init:
+ * For each in/out fd pair a threaded_reader and threaded_writer thread is
+ * created. Within the DriverData struct each of the threads have an AsyncIO
+ * sctruct associated with it. Within AsyncIO there are two important HANDLEs,
+ * ioAllowed and ov.hEvent. ioAllowed is used to signal the threaded_* threads
+ * should read/write some data, and ov.hEvent is driver_select'ed to be used to
+ * signal that the thread is done reading/writing.
+ *
+ * The reason for the driver being threaded like this is because once the FD is open
+ * on windows, it is not possible to set the it in overlapped mode. So we have to
+ * simulate this using threads.
+ *
+ * output:
+ * When an output occurs the data to be outputted is copied to AsyncIO.ov. Then
+ * the ioAllowed HANDLE is set, ov.hEvent is cleared and the port is marked as busy.
+ * The threaded_writer thread is lying in WaitForMultipleObjects on ioAllowed, and
+ * when signalled it writes all data in AsyncIO.ov and then sets ov.hEvent so that
+ * ready_output gets triggered and (potentially) sends the reply to the port and
+ * marks the port an non-busy.
+ *
+ * input:
+ * The threaded_reader is lying waiting in ReadFile on the in fd and when a new
+ * line is written it sets ov.hEvent that new data is available and then goes
+ * and waits for ioAllowed to be set. ready_input is run when ov.hEvent is set and
+ * delivers the data to the port. Then ioAllowed is signalled again and threaded_reader
+ * goes back to ReadFile.
+ *
+ * shutdown:
+ * In order to guarantee that all io is outputted before the driver is stopped,
+ * fd_stop uses flushEvent and flushReplyEvent to make sure that there is no data
+ * in ov which needs writing before returning from fd_stop.
+ *
+ **/
+
if (!create_file_thread(&dp->in, DO_READ)) {
dp->port_num = PORT_FREE;
return ERL_DRV_ERROR_GENERAL;
@@ -2241,6 +2325,8 @@ static void fd_stop(ErlDrvData d)
(void) driver_select(dp->port_num,
(ErlDrvEvent)dp->out.ov.hEvent,
ERL_DRV_WRITE, 0);
+ SetEvent(dp->out.flushEvent);
+ WaitForSingleObject(dp->out.flushReplyEvent, INFINITE);
}
}