Skip to content

Commit 1e68e98

Browse files
committed
[erts][socket] Add {otp, scheduler_polling} option
1 parent c89cbee commit 1e68e98

File tree

11 files changed

+160
-6
lines changed

11 files changed

+160
-6
lines changed

erts/doc/references/erl_driver.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -433,6 +433,7 @@ first:
433433
int nif_major_version;
434434
int nif_minor_version;
435435
int dirty_scheduler_support;
436+
int scheduler_polling_support;
436437
} ErlDrvSysInfo;
437438
```
438439
@@ -487,6 +488,12 @@ first:
487488
- **`dirty_scheduler_support`** - A value `!= 0` if the runtime system has
488489
support for dirty scheduler threads; otherwise `0`.
489490
491+
- **`scheduler_polling_support`** - A value `!= 0` if the runtime system has
492+
support for scheduler pollset migration (available on platforms with epoll
493+
or kqueue); otherwise `0`. When supported, file descriptors used by NIFs
494+
may be automatically migrated to scheduler-specific pollsets after repeated
495+
use from the same process.
496+
490497
- **`ErlDrvBinary`{: #ErlDrvBinary }**
491498
492499
```text

erts/emulator/beam/erl_driver.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@
7070

7171
#define ERL_DRV_EXTENDED_MARKER (0xfeeeeeed)
7272
#define ERL_DRV_EXTENDED_MAJOR_VERSION 3
73-
#define ERL_DRV_EXTENDED_MINOR_VERSION 3
73+
#define ERL_DRV_EXTENDED_MINOR_VERSION 4
7474

7575
/*
7676
* The emulator will refuse to load a driver with a major version

erts/emulator/beam/erl_drv_nif.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ typedef struct {
3939
int nif_major_version;
4040
int nif_minor_version;
4141
int dirty_scheduler_support;
42+
int scheduler_polling_support;
4243
} ErlDrvSysInfo;
4344

4445
typedef struct {
@@ -58,7 +59,8 @@ enum ErlNifSelectFlags {
5859
ERL_NIF_SELECT_STOP = (1 << 2),
5960
ERL_NIF_SELECT_CANCEL = (1 << 3),
6061
ERL_NIF_SELECT_CUSTOM_MSG= (1 << 4),
61-
ERL_NIF_SELECT_ERROR = (1 << 5)
62+
ERL_NIF_SELECT_ERROR = (1 << 5),
63+
ERL_NIF_SELECT_NO_SCHEDULER_POLLSET = (1 << 6)
6264
};
6365

6466
/*

erts/emulator/beam/io.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
#include "erl_nif.h"
3838

3939
#include "erl_vm.h"
40+
#include "erl_poll.h"
4041
#include "global.h"
4142
#include "erl_process.h"
4243
#include "dist.h"
@@ -7486,6 +7487,17 @@ driver_system_info(ErlDrvSysInfo *sip, size_t si_size)
74867487
1
74877488
;
74887489
}
7490+
/*
7491+
* 'scheduler_polling_support' is the last field in the 5th version
7492+
* (driver version 3.2, NIF version 2.18)
7493+
*/
7494+
if (si_size >= ERL_DRV_SYS_INFO_SIZE(scheduler_polling_support)) {
7495+
#if ERTS_POLL_USE_SCHEDULER_POLLING
7496+
sip->scheduler_polling_support = 1;
7497+
#else
7498+
sip->scheduler_polling_support = 0;
7499+
#endif
7500+
}
74897501

74907502
}
74917503

erts/emulator/nifs/common/prim_socket_int.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -522,6 +522,7 @@ typedef struct {
522522
SOCKET origFD; // A 'socket' created from this FD
523523
BOOLEAN_T closeOnClose; // Have we dup'ed or not
524524
BOOLEAN_T selectRead; // Try to have read select active
525+
BOOLEAN_T schedulerPolling; // Allow scheduler pollset migration
525526
/* +++ The dbg flag for SSDBG +++ */
526527
BOOLEAN_T dbg;
527528
BOOLEAN_T useReg;

erts/emulator/nifs/common/prim_socket_nif.c

Lines changed: 108 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -869,6 +869,7 @@ const int esock_ioctl_flags_length = NUM(esock_ioctl_flags);
869869
#define ESOCK_OPT_OTP_META 1009
870870
#define ESOCK_OPT_OTP_USE_REGISTRY 1010
871871
#define ESOCK_OPT_OTP_SELECT_READ 1011
872+
#define ESOCK_OPT_OTP_SCHEDULER_POLLING 1012
872873
/**/
873874
#define ESOCK_OPT_OTP_DOMAIN 1999 // INTERNAL AND ONLY GET
874875
#if 0
@@ -1243,6 +1244,7 @@ static ERL_NIF_TERM esock_setopt_otp(ErlNifEnv* env,
12431244
ESOCK_SETOPT_OTP_FUNC_DEF(iow); \
12441245
ESOCK_SETOPT_OTP_FUNC_DEF(ctrl_proc); \
12451246
ESOCK_SETOPT_OTP_FUNC_DEF(select_read); \
1247+
ESOCK_SETOPT_OTP_FUNC_DEF(scheduler_polling); \
12461248
ESOCK_SETOPT_OTP_FUNC_DEF(rcvbuf); \
12471249
ESOCK_SETOPT_OTP_FUNC_DEF(rcvctrlbuf); \
12481250
ESOCK_SETOPT_OTP_FUNC_DEF(sndctrlbuf); \
@@ -1278,6 +1280,7 @@ static ERL_NIF_TERM esock_getopt_otp(ErlNifEnv* env,
12781280
ESOCK_GETOPT_OTP_FUNC_DEF(iow); \
12791281
ESOCK_GETOPT_OTP_FUNC_DEF(ctrl_proc); \
12801282
ESOCK_GETOPT_OTP_FUNC_DEF(select_read); \
1283+
ESOCK_GETOPT_OTP_FUNC_DEF(scheduler_polling); \
12811284
ESOCK_GETOPT_OTP_FUNC_DEF(rcvbuf); \
12821285
ESOCK_GETOPT_OTP_FUNC_DEF(rcvctrlbuf); \
12831286
ESOCK_GETOPT_OTP_FUNC_DEF(sndctrlbuf); \
@@ -6792,6 +6795,12 @@ ERL_NIF_TERM esock_setopt_otp(ErlNifEnv* env,
67926795
MUNLOCK(descP->readMtx);
67936796
break;
67946797

6798+
case ESOCK_OPT_OTP_SCHEDULER_POLLING:
6799+
MLOCK(descP->readMtx);
6800+
result = esock_setopt_otp_scheduler_polling(env, descP, eVal);
6801+
MUNLOCK(descP->readMtx);
6802+
break;
6803+
67956804
case ESOCK_OPT_OTP_RCVBUF:
67966805
MLOCK(descP->readMtx);
67976806
result = esock_setopt_otp_rcvbuf(env, descP, eVal);
@@ -6929,6 +6938,51 @@ ERL_NIF_TERM esock_setopt_otp_select_read(ErlNifEnv* env,
69296938

69306939

69316940

6941+
/* esock_setopt_otp_scheduler_polling - Handle the OTP (level) scheduler_polling option
6942+
*/
6943+
6944+
static
6945+
ERL_NIF_TERM esock_setopt_otp_scheduler_polling(ErlNifEnv* env,
6946+
ESockDescriptor* descP,
6947+
ERL_NIF_TERM eVal)
6948+
{
6949+
BOOLEAN_T schedulerPolling;
6950+
6951+
if (! IS_OPEN(descP->readState)) {
6952+
SSDBG( descP,
6953+
("SOCKET", "esock_setopt_otp_scheduler_polling {%d} -> closed\r\n",
6954+
descP->sock) );
6955+
return esock_make_error_closed(env);
6956+
}
6957+
6958+
if (! esock_decode_bool(eVal, &schedulerPolling))
6959+
return esock_make_invalid(env, esock_atom_value);
6960+
6961+
/* Check if scheduler polling is supported on this platform */
6962+
{
6963+
ErlNifSysInfo si;
6964+
enif_system_info(&si, sizeof(ErlNifSysInfo));
6965+
if (!si.scheduler_polling_support && schedulerPolling) {
6966+
SSDBG( descP,
6967+
("SOCKET", "esock_setopt_otp_scheduler_polling {%d} -> notsup"
6968+
"\r\n scheduler polling not available on this platform"
6969+
"\r\n", descP->sock) );
6970+
return esock_make_error(env, esock_atom_enotsup);
6971+
}
6972+
}
6973+
6974+
descP->schedulerPolling = schedulerPolling;
6975+
6976+
SSDBG( descP,
6977+
("SOCKET", "esock_setopt_otp_scheduler_polling {%d} -> ok"
6978+
"\r\n eVal: %T"
6979+
"\r\n", descP->sock, eVal) );
6980+
6981+
return esock_atom_ok;
6982+
}
6983+
6984+
6985+
69326986
/* esock_setopt_otp_ctrl_proc - Handle the OTP (level)
69336987
* controlling_process options
69346988
*/
@@ -8589,6 +8643,12 @@ ERL_NIF_TERM esock_getopt_otp(ErlNifEnv* env,
85898643
MUNLOCK(descP->readMtx);
85908644
break;
85918645

8646+
case ESOCK_OPT_OTP_SCHEDULER_POLLING:
8647+
MLOCK(descP->readMtx);
8648+
result = esock_getopt_otp_scheduler_polling(env, descP);
8649+
MUNLOCK(descP->readMtx);
8650+
break;
8651+
85928652
case ESOCK_OPT_OTP_RCVBUF:
85938653
MLOCK(descP->readMtx);
85948654
result = esock_getopt_otp_rcvbuf(env, descP);
@@ -8751,6 +8811,34 @@ ERL_NIF_TERM esock_getopt_otp_select_read(ErlNifEnv* env,
87518811

87528812

87538813

8814+
/* esock_getopt_otp_scheduler_polling - Handle the OTP (level) scheduler_polling option
8815+
*/
8816+
8817+
static
8818+
ERL_NIF_TERM esock_getopt_otp_scheduler_polling(ErlNifEnv* env,
8819+
ESockDescriptor* descP)
8820+
{
8821+
ERL_NIF_TERM eVal;
8822+
8823+
if (! IS_OPEN(descP->readState)) {
8824+
SSDBG( descP,
8825+
("SOCKET", "esock_getopt_otp_scheduler_polling {%d} -> done closed\r\n",
8826+
descP->sock) );
8827+
return esock_make_error_closed(env);
8828+
}
8829+
8830+
eVal = esock_encode_bool(descP->schedulerPolling);
8831+
8832+
SSDBG( descP,
8833+
("SOCKET", "esock_getopt_otp_scheduler_polling {%d} ->"
8834+
"\r\n eVal: %T"
8835+
"\r\n", descP->sock, eVal) );
8836+
8837+
return esock_make_ok2(env, eVal);
8838+
}
8839+
8840+
8841+
87548842
/* esock_getopt_otp_ctrl_proc - Handle the OTP (level) controlling_process option
87558843
*/
87568844

@@ -12057,6 +12145,12 @@ ESockDescriptor* esock_alloc_descriptor(SOCKET sock)
1205712145
descP->iow = FALSE;
1205812146
descP->dbg = ESOCK_DEBUG_DEFAULT; // Overwritten by caller
1205912147
descP->selectRead = FALSE;
12148+
{
12149+
/* Query scheduler polling support at runtime */
12150+
ErlNifSysInfo si;
12151+
enif_system_info(&si, sizeof(ErlNifSysInfo));
12152+
descP->schedulerPolling = si.scheduler_polling_support ? TRUE : FALSE;
12153+
}
1206012154
descP->useReg = ESOCK_USE_SOCKET_REGISTRY;// Overwritten by caller
1206112155
descP->meta.env = esock_alloc_env("esock_alloc_descriptor - "
1206212156
"meta-env");
@@ -12692,9 +12786,15 @@ int esock_select_read(ErlNifEnv* env,
1269212786
ERL_NIF_TERM sockRef, // Socket
1269312787
ERL_NIF_TERM selectRef) // "ID" of the operation
1269412788
{
12789+
ESockDescriptor* descP = (ESockDescriptor*) obj;
1269512790
ERL_NIF_TERM selectMsg = mk_select_msg(env, sockRef, selectRef);
12791+
enum ErlNifSelectFlags flags = ERL_NIF_SELECT_READ | ERL_NIF_SELECT_CUSTOM_MSG;
12792+
12793+
if (!descP->schedulerPolling) {
12794+
flags |= ERL_NIF_SELECT_NO_SCHEDULER_POLLSET;
12795+
}
1269612796

12697-
return enif_select_read(env, event, obj, pidP, selectMsg, NULL);
12797+
return enif_select_x(env, event, flags, obj, pidP, selectMsg, NULL);
1269812798

1269912799
}
1270012800
#endif // #ifndef __WIN32__
@@ -12716,9 +12816,15 @@ int esock_select_write(ErlNifEnv* env,
1271612816
ERL_NIF_TERM sockRef, // Socket
1271712817
ERL_NIF_TERM selectRef) // "ID" of the operation
1271812818
{
12819+
ESockDescriptor* descP = (ESockDescriptor*) obj;
1271912820
ERL_NIF_TERM selectMsg = mk_select_msg(env, sockRef, selectRef);
12821+
enum ErlNifSelectFlags flags = ERL_NIF_SELECT_WRITE | ERL_NIF_SELECT_CUSTOM_MSG;
12822+
12823+
if (!descP->schedulerPolling) {
12824+
flags |= ERL_NIF_SELECT_NO_SCHEDULER_POLLSET;
12825+
}
1272012826

12721-
return enif_select_write(env, event, obj, pidP, selectMsg, NULL);
12827+
return enif_select_x(env, event, flags, obj, pidP, selectMsg, NULL);
1272212828
}
1272312829
#endif // #ifndef __WIN32__
1272412830

erts/emulator/sys/common/erl_check_io.c

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,10 +90,12 @@ typedef enum {
9090
ERTS_EV_FLAG_IN_SCHEDULER = 0x4, /* Set when the fd is currently in
9191
scheduler pollset */
9292
ERTS_EV_FLAG_NIF_SELECT = 0x8, /* Set if a nif select message is in-flight */
93+
ERTS_EV_FLAG_NO_SCHEDULER_POLLSET = 0x40, /* Disable scheduler pollset migration */
9394
#else
9495
ERTS_EV_FLAG_SCHEDULER = ERTS_EV_FLAG_CLEAR,
9596
ERTS_EV_FLAG_IN_SCHEDULER = ERTS_EV_FLAG_CLEAR,
9697
ERTS_EV_FLAG_NIF_SELECT = ERTS_EV_FLAG_CLEAR,
98+
ERTS_EV_FLAG_NO_SCHEDULER_POLLSET = ERTS_EV_FLAG_CLEAR,
9799
#endif
98100
#ifdef ERTS_POLL_USE_FALLBACK
99101
ERTS_EV_FLAG_FALLBACK = 0x10, /* Set when kernel poll rejected fd
@@ -1351,10 +1353,18 @@ enif_select_x(ErlNifEnv* env,
13511353
if (state->type == ERTS_EV_TYPE_NONE)
13521354
ctl_op = ERTS_POLL_OP_ADD;
13531355
#if ERTS_POLL_USE_SCHEDULER_POLLING
1354-
else if (ctl_events & ERTS_POLL_EV_IN) {
1356+
/* Handle the NO_SCHEDULER_POLLSET flag */
1357+
if (mode & ERL_NIF_SELECT_NO_SCHEDULER_POLLSET) {
1358+
state->flags |= ERTS_EV_FLAG_NO_SCHEDULER_POLLSET;
1359+
} else {
1360+
state->flags &= ~ERTS_EV_FLAG_NO_SCHEDULER_POLLSET;
1361+
}
1362+
1363+
if (ctl_events & ERTS_POLL_EV_IN) {
13551364
if ((state->flags & (ERTS_EV_FLAG_SCHEDULER |
13561365
ERTS_EV_FLAG_FALLBACK |
1357-
ERTS_EV_FLAG_NIF_SELECT)) == ERTS_EV_FLAG_NIF_SELECT
1366+
ERTS_EV_FLAG_NIF_SELECT |
1367+
ERTS_EV_FLAG_NO_SCHEDULER_POLLSET)) == ERTS_EV_FLAG_NIF_SELECT
13581368
&& erts_sched_poll_enabled()) {
13591369
/* Check if this is a different process than last time.
13601370
* If so, reset the counter to prevent scheduler pollset migration

erts/preloaded/ebin/init.beam

12 Bytes
Binary file not shown.
32 Bytes
Binary file not shown.

erts/preloaded/src/prim_socket.erl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@
102102
-define(ESOCK_OPT_OTP_META, 1009).
103103
-define(ESOCK_OPT_OTP_USE_REGISTRY, 1010).
104104
-define(ESOCK_OPT_OTP_SELECT_READ, 1011).
105+
-define(ESOCK_OPT_OTP_SCHEDULER_POLLING, 1012).
105106
%%
106107
-define(ESOCK_OPT_OTP_DOMAIN, 1999). % INTERNAL
107108
%%-define(ESOCK_OPT_OTP_TYPE, 1998). % INTERNAL
@@ -1160,6 +1161,7 @@ enc_sockopt({otp = Level, Opt}, 0 = _NativeValue) ->
11601161
meta -> ?ESOCK_OPT_OTP_META;
11611162
use_registry -> ?ESOCK_OPT_OTP_USE_REGISTRY;
11621163
select_read -> ?ESOCK_OPT_OTP_SELECT_READ;
1164+
scheduler_polling -> ?ESOCK_OPT_OTP_SCHEDULER_POLLING;
11631165
domain -> ?ESOCK_OPT_OTP_DOMAIN;
11641166
_ -> invalid
11651167
end

0 commit comments

Comments
 (0)