Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
  • 1 commit
  • 15 files changed
  • 0 comments
  • 1 contributor
Sep 05, 2013
Steve Vinoski add {active,N} socket option for TCP, UDP, and SCTP
Add the {active,N} socket option, where N is an integer in the range
-32768..32767, to allow a caller to specify the number of data messages to
be delivered to the controlling process. Once the socket's delivered
message count either reaches 0 or is explicitly set to 0 with
inet:setopts/2 or by including {active,0} as an option when the socket is
created, the socket transitions to passive ({active, false}) mode and the
socket's controlling process receives a message to inform it of the
transition. TCP sockets receive {tcp_passive,Socket}, UDP sockets receive
{udp_passive,Socket} and SCTP sockets receive {sctp_passive,Socket}.

The socket's delivered message counter defaults to 0, but it can be set
using {active,N} via any gen_tcp, gen_udp, or gen_sctp function that takes
socket options as arguments, or via inet:setopts/2. New N values are added
to the socket's current counter value, and negative numbers can be used to
reduce the counter value. Specifying a number that would cause the socket's
counter value to go above 32767 causes an einval error. If a negative
number is specified such that the counter value would become negative, the
socket's counter value is set to 0 and the socket transitions to passive
mode. If the counter value is already 0 and inet:setopts(Socket,
[{active,0}]) is specified, the counter value remains at 0 but the
appropriate passive mode transition message is generated for the socket.

This commit contains a modified preloaded prim_inet.beam due to changes in
prim_inet.erl.

Add tests for {active,N} mode for TCP, UDP, and SCTP sockets.

Add documentation for {active,N} mode for inet, gen_tcp, gen_udp, and
gen_sctp.
c80a762
119  erts/emulator/drivers/common/inet_drv.c
@@ -86,6 +86,13 @@
86 86
 #endif
87 87
 typedef unsigned long long llu_t;
88 88
 
  89
+#ifndef INT16_MIN
  90
+#define INT16_MIN (-32768)
  91
+#endif
  92
+#ifndef INT16_MAX
  93
+#define INT16_MAX (32767)
  94
+#endif
  95
+
89 96
 #ifdef __WIN32__
90 97
 #define  STRNCASECMP strncasecmp
91 98
 
@@ -581,6 +588,7 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n)
581 588
 #define INET_PASSIVE        0  /* false */
582 589
 #define INET_ACTIVE         1  /* true */
583 590
 #define INET_ONCE           2  /* true; active once then passive */
  591
+#define INET_MULTI          3  /* true; active N then passive */
584 592
 
585 593
 /* INET_REQ_GETSTATUS enumeration */
586 594
 #define INET_F_OPEN         0x0001
@@ -925,6 +933,7 @@ typedef struct {
925 933
     inet_async_op  op_queue[INET_MAX_ASYNC];  /* call queue */
926 934
 
927 935
     int   active;               /* 0 = passive, 1 = active, 2 = active once */
  936
+    Sint16 active_count;        /* counter for {active,N} */
928 937
     int   stype;                /* socket type:
929 938
 				    SOCK_STREAM/SOCK_DGRAM/SOCK_SEQPACKET   */
930 939
     int   sprotocol;            /* socket protocol:
@@ -1160,22 +1169,36 @@ static int packet_inet_output(udp_descriptor* udesc, HANDLE event);
1160 1169
 static int async_ref = 0;          /* async reference id generator */
1161 1170
 #define NEW_ASYNC_ID() ((async_ref++) & 0xffff)
1162 1171
 
  1172
+/* check for transition from active to passive */
  1173
+#define INET_CHECK_ACTIVE_TO_PASSIVE(inet)                              \
  1174
+    do {                                                                \
  1175
+        if ((inet)->active == INET_ONCE)                                \
  1176
+            (inet)->active = INET_PASSIVE;                              \
  1177
+        else if ((inet)->active == INET_MULTI && --((inet)->active_count) == 0) { \
  1178
+            (inet)->active = INET_PASSIVE;                              \
  1179
+            packet_passive_message(inet);                               \
  1180
+        }                                                               \
  1181
+    } while (0)
1163 1182
 
1164 1183
 static ErlDrvTermData am_ok;
1165 1184
 static ErlDrvTermData am_tcp;
1166 1185
 static ErlDrvTermData am_udp;
1167 1186
 static ErlDrvTermData am_error;
  1187
+static ErlDrvTermData am_einval;
1168 1188
 static ErlDrvTermData am_inet_async;
1169 1189
 static ErlDrvTermData am_inet_reply;
1170 1190
 static ErlDrvTermData am_timeout;
1171 1191
 static ErlDrvTermData am_closed;
  1192
+static ErlDrvTermData am_tcp_passive;
1172 1193
 static ErlDrvTermData am_tcp_closed;
1173 1194
 static ErlDrvTermData am_tcp_error;
  1195
+static ErlDrvTermData am_udp_passive;
1174 1196
 static ErlDrvTermData am_udp_error;
1175 1197
 static ErlDrvTermData am_empty_out_q;
1176 1198
 static ErlDrvTermData am_ssl_tls;
1177 1199
 #ifdef HAVE_SCTP
1178 1200
 static ErlDrvTermData am_sctp;
  1201
+static ErlDrvTermData am_sctp_passive;
1179 1202
 static ErlDrvTermData am_sctp_error;
1180 1203
 static ErlDrvTermData am_true;
1181 1204
 static ErlDrvTermData am_false;
@@ -1185,6 +1208,7 @@ static ErlDrvTermData am_list;
1185 1208
 static ErlDrvTermData am_binary;
1186 1209
 static ErlDrvTermData am_active;
1187 1210
 static ErlDrvTermData am_once;
  1211
+static ErlDrvTermData am_multi;
1188 1212
 static ErlDrvTermData am_buffer;
1189 1213
 static ErlDrvTermData am_linger;
1190 1214
 static ErlDrvTermData am_recbuf;
@@ -3337,6 +3361,34 @@ static int packet_binary_message
3337 3361
 }
3338 3362
 
3339 3363
 /*
  3364
+** active mode message: send active-to-passive transition message
  3365
+**        {tcp_passive, S} or
  3366
+**        {udp_passive, S} or
  3367
+**        {sctp_passive, S}
  3368
+*/
  3369
+ static int packet_passive_message(inet_descriptor* desc)
  3370
+ {
  3371
+     ErlDrvTermData spec[6];
  3372
+     int i = 0;
  3373
+
  3374
+     DEBUGF(("packet_passive_message(%ld):\r\n", (long)desc->port));
  3375
+
  3376
+     if (desc->sprotocol == IPPROTO_TCP)
  3377
+         i = LOAD_ATOM(spec, i, am_tcp_passive);
  3378
+     else {
  3379
+#ifdef HAVE_SCTP
  3380
+         i = LOAD_ATOM(spec, i, IS_SCTP(desc) ? am_sctp_passive : am_udp_passive);
  3381
+#else
  3382
+         i = LOAD_ATOM(spec, i, am_udp_passive);
  3383
+#endif
  3384
+     }
  3385
+     i = LOAD_PORT(spec, i, desc->dport);
  3386
+     i = LOAD_TUPLE(spec, i, 2);
  3387
+     ASSERT(i <= 6);
  3388
+     return erl_drv_output_term(desc->dport, spec, i);
  3389
+ }
  3390
+
  3391
+/*
3340 3392
 ** send active message {udp_error|sctp_error, S, Error}
3341 3393
 */
3342 3394
 static int packet_error_message(udp_descriptor* udesc, int err)
@@ -3378,7 +3430,7 @@ static int tcp_reply_data(tcp_descriptor* desc, char* buf, int len)
3378 3430
     int code;
3379 3431
     const char* body = buf;
3380 3432
     int bodylen = len;
3381  
-    
  3433
+
3382 3434
     packet_get_body(desc->inet.htype, &body, &bodylen);
3383 3435
 
3384 3436
     if (desc->inet.deliver == INET_DELIVER_PORT) {
@@ -3396,8 +3448,7 @@ static int tcp_reply_data(tcp_descriptor* desc, char* buf, int len)
3396 3448
 
3397 3449
     if (code < 0)
3398 3450
 	return code;
3399  
-    if (desc->inet.active == INET_ONCE)
3400  
-	desc->inet.active = INET_PASSIVE;
  3451
+    INET_CHECK_ACTIVE_TO_PASSIVE(INETP(desc));
3401 3452
     return code;
3402 3453
 }
3403 3454
 
@@ -3424,8 +3475,7 @@ tcp_reply_binary_data(tcp_descriptor* desc, ErlDrvBinary* bin, int offs, int len
3424 3475
     }
3425 3476
     if (code < 0)
3426 3477
 	return code;
3427  
-    if (desc->inet.active == INET_ONCE)
3428  
-	desc->inet.active = INET_PASSIVE;
  3478
+    INET_CHECK_ACTIVE_TO_PASSIVE(INETP(desc));
3429 3479
     return code;
3430 3480
 }
3431 3481
 
@@ -3448,8 +3498,7 @@ packet_reply_binary_data(inet_descriptor* desc, unsigned  int hsz,
3448 3498
 	    code = packet_binary_message(desc, bin, offs, len, extra);
3449 3499
 	if (code < 0)
3450 3500
 	    return code;
3451  
-	if (desc->active == INET_ONCE)
3452  
-	    desc->active = INET_PASSIVE;
  3501
+        INET_CHECK_ACTIVE_TO_PASSIVE(desc);
3453 3502
 	return code;
3454 3503
     }
3455 3504
 }
@@ -3494,6 +3543,7 @@ sock_init(void) /* May be called multiple times. */
3494 3543
 #ifdef HAVE_SCTP
3495 3544
 static void inet_init_sctp(void) {
3496 3545
     INIT_ATOM(sctp);
  3546
+    INIT_ATOM(sctp_passive);
3497 3547
     INIT_ATOM(sctp_error);
3498 3548
     INIT_ATOM(true);
3499 3549
     INIT_ATOM(false);
@@ -3503,6 +3553,7 @@ static void inet_init_sctp(void) {
3503 3553
     INIT_ATOM(binary);
3504 3554
     INIT_ATOM(active);
3505 3555
     INIT_ATOM(once);
  3556
+    INIT_ATOM(multi);
3506 3557
     INIT_ATOM(buffer);
3507 3558
     INIT_ATOM(linger);
3508 3559
     INIT_ATOM(recbuf);
@@ -3628,12 +3679,15 @@ static int inet_init()
3628 3679
     INIT_ATOM(tcp);
3629 3680
     INIT_ATOM(udp);
3630 3681
     INIT_ATOM(error);
  3682
+    INIT_ATOM(einval);
3631 3683
     INIT_ATOM(inet_async);
3632 3684
     INIT_ATOM(inet_reply);
3633 3685
     INIT_ATOM(timeout);
3634 3686
     INIT_ATOM(closed);
  3687
+    INIT_ATOM(tcp_passive);
3635 3688
     INIT_ATOM(tcp_closed);
3636 3689
     INIT_ATOM(tcp_error);
  3690
+    INIT_ATOM(udp_passive);
3637 3691
     INIT_ATOM(udp_error);
3638 3692
     INIT_ATOM(empty_out_q);
3639 3693
     INIT_ATOM(ssl_tls);
@@ -5508,8 +5562,25 @@ static int inet_set_opts(inet_descriptor* desc, char* ptr, int len)
5508 5562
 
5509 5563
 	case INET_LOPT_ACTIVE:
5510 5564
 	    DEBUGF(("inet_set_opts(%ld): s=%d, ACTIVE=%d\r\n",
5511  
-		    (long)desc->port, desc->s,ival));
  5565
+		    (long)desc->port, desc->s, ival));
5512 5566
 	    desc->active = ival;
  5567
+            if (desc->active == INET_MULTI) {
  5568
+                long ac = desc->active_count;
  5569
+                Sint16 nval = get_int16(ptr);
  5570
+                ptr += 2;
  5571
+                len -= 2;
  5572
+                ac += nval;
  5573
+                if (ac > INT16_MAX || ac < INT16_MIN)
  5574
+                    return -1;
  5575
+                desc->active_count += nval;
  5576
+                if (desc->active_count < 0)
  5577
+                    desc->active_count = 0;
  5578
+                if (desc->active_count == 0) {
  5579
+                    desc->active = INET_PASSIVE;
  5580
+                    packet_passive_message(desc);
  5581
+                }
  5582
+            } else
  5583
+                desc->active_count = 0;
5513 5584
 	    if ((desc->stype == SOCK_STREAM) && (desc->active != INET_PASSIVE) && 
5514 5585
 		(desc->state == INET_STATE_CLOSED)) {
5515 5586
 		tcp_closed_message((tcp_descriptor *) desc);
@@ -5820,7 +5891,8 @@ static int inet_set_opts(inet_descriptor* desc, char* ptr, int len)
5820 5891
 		/* XXX fprintf(stderr,"desc->htype == %d, old_htype == %d, 
5821 5892
 		   desc->active == %d, old_active == %d\r\n",(int)desc->htype, 
5822 5893
 		   (int) old_htype, (int) desc->active, (int) old_active );*/
5823  
-		return 1+(desc->htype == old_htype && desc->active == INET_ONCE);
  5894
+		return 1+(desc->htype == old_htype &&
  5895
+                          (desc->active == INET_ONCE || desc->active == INET_MULTI));
5824 5896
 	    }
5825 5897
 	    return 0;
5826 5898
 	}
@@ -5953,6 +6025,21 @@ static int sctp_set_opts(inet_descriptor* desc, char* ptr, int len)
5953 6025
 
5954 6026
 	case INET_LOPT_ACTIVE:
5955 6027
 	    desc->active = get_int32(curr);		curr += 4;
  6028
+            if (desc->active == INET_MULTI) {
  6029
+                long ac = desc->active_count;
  6030
+                Sint16 nval = get_int16(curr);          curr += 2;
  6031
+		ac += nval;
  6032
+                if (ac > INT16_MAX || ac < INT16_MIN)
  6033
+                    return -1;
  6034
+                desc->active_count += nval;
  6035
+                if (desc->active_count < 0)
  6036
+                    desc->active_count = 0;
  6037
+                if (desc->active_count == 0) {
  6038
+                    desc->active = INET_PASSIVE;
  6039
+                    packet_passive_message(desc);
  6040
+                }
  6041
+            } else
  6042
+                desc->active_count = 0;
5956 6043
 	    res = 0;
5957 6044
 	    continue;
5958 6045
 
@@ -6475,6 +6562,11 @@ static ErlDrvSSizeT inet_fill_opts(inet_descriptor* desc,
6475 6562
 	case INET_LOPT_ACTIVE:
6476 6563
 	    *ptr++ = opt;
6477 6564
 	    put_int32(desc->active, ptr);
  6565
+            if (desc->active == INET_MULTI) {
  6566
+                PLACE_FOR(2,ptr);
  6567
+                put_int16(desc->active_count, ptr);
  6568
+                ptr += 2;
  6569
+            }
6478 6570
 	    continue;
6479 6571
 	case INET_LOPT_PACKET:
6480 6572
 	    *ptr++ = opt;
@@ -6847,7 +6939,10 @@ static ErlDrvSSizeT sctp_fill_opts(inet_descriptor* desc,
6847 6939
 	}
6848 6940
 	case INET_LOPT_ACTIVE:
6849 6941
 	{
6850  
-	    PLACE_FOR(spec, i, 2*LOAD_ATOM_CNT + LOAD_TUPLE_CNT);
  6942
+            if (desc->active == INET_MULTI)
  6943
+                PLACE_FOR(spec, i, LOAD_ATOM_CNT + LOAD_INT_CNT + LOAD_TUPLE_CNT);
  6944
+            else
  6945
+                PLACE_FOR(spec, i, 2*LOAD_ATOM_CNT + LOAD_TUPLE_CNT);
6851 6946
 	    i = LOAD_ATOM (spec, i, am_active);
6852 6947
 	    switch (desc->active)
6853 6948
 	    {
@@ -6860,6 +6955,9 @@ static ErlDrvSSizeT sctp_fill_opts(inet_descriptor* desc,
6860 6955
 		case INET_ONCE   :
6861 6956
 		{ i = LOAD_ATOM (spec, i, am_once);  break; }
6862 6957
 
  6958
+                case INET_MULTI  :
  6959
+                { i = LOAD_INT(spec, i, desc->active_count); break; }
  6960
+
6863 6961
 		default: ASSERT (0);
6864 6962
 	    }
6865 6963
 	    i = LOAD_TUPLE (spec, i, 2);
@@ -7656,6 +7754,7 @@ static ErlDrvData inet_start(ErlDrvPort port, int size, int protocol)
7656 7754
 					  socket */
7657 7755
     desc->deliver = INET_DELIVER_TERM; /* standard term format */
7658 7756
     desc->active  = INET_PASSIVE;      /* start passive */
  7757
+    desc->active_count = 0;
7659 7758
     desc->oph = NULL;
7660 7759
     desc->opt = NULL;
7661 7760
 
BIN  erts/preloaded/ebin/prim_inet.beam
Binary file not shown
20  erts/preloaded/src/prim_inet.erl
@@ -1237,7 +1237,8 @@ type_opt_1(buffer)          -> int;
1237 1237
 type_opt_1(active) ->
1238 1238
     {enum,[{false, ?INET_PASSIVE}, 
1239 1239
 	   {true, ?INET_ACTIVE}, 
1240  
-	   {once, ?INET_ONCE}]};
  1240
+	   {once, ?INET_ONCE},
  1241
+           {multi, ?INET_MULTI}]};
1241 1242
 type_opt_1(packet) -> 
1242 1243
     {enum,[{0, ?TCP_PB_RAW},
1243 1244
 	   {1, ?TCP_PB_1},
@@ -1716,11 +1717,14 @@ encode_opt_val(Opts) ->
1716 1717
 	Reason -> {error,Reason}
1717 1718
     end.
1718 1719
 
  1720
+%% {active, once} and {active, N} are specially optimized because they will
  1721
+%% be used for every packet or every N packets, not only once when
  1722
+%% initializing the socket.  Measurements show that this optimization is
  1723
+%% worthwhile.
1719 1724
 enc_opt_val([{active,once}|Opts], Acc) ->
1720  
-    %% Specially optimized because {active,once} will be used for
1721  
-    %% every packet, not only once when initializing the socket.
1722  
-    %% Measurements show that this optimization is worthwhile.
1723 1725
     enc_opt_val(Opts, [<<?INET_LOPT_ACTIVE:8,?INET_ONCE:32>>|Acc]);
  1726
+enc_opt_val([{active,N}|Opts], Acc) when is_integer(N), N < 32768, N >= -32768 ->
  1727
+    enc_opt_val(Opts, [<<?INET_LOPT_ACTIVE:8,?INET_MULTI:32,N:16>>|Acc]);
1724 1728
 enc_opt_val([{raw,P,O,B}|Opts], Acc) ->
1725 1729
     enc_opt_val(Opts, Acc, raw, {P,O,B});
1726 1730
 enc_opt_val([{Opt,Val}|Opts], Acc) ->
@@ -1810,6 +1814,14 @@ dec_opt_val([]) -> [].
1810 1814
 dec_opt_val(Buf, raw, Type) ->
1811 1815
     {{P,O,B},T} = dec_value(Type, Buf),
1812 1816
     [{raw,P,O,B}|dec_opt_val(T)];
  1817
+dec_opt_val(Buf, active, Type) ->
  1818
+    case dec_value(Type, Buf) of
  1819
+        {multi,[M0,M1|T]} ->
  1820
+            <<N:16>> = list_to_binary([M0,M1]),
  1821
+            [{active,N}|dec_opt_val(T)];
  1822
+        {Val,T} ->
  1823
+            [{active,Val}|dec_opt_val(T)]
  1824
+    end;
1813 1825
 dec_opt_val(Buf, Opt, Type) ->
1814 1826
     {Val,T} = dec_value(Type, Buf),
1815 1827
     [{Opt,Val}|dec_opt_val(T)].
31  lib/kernel/doc/src/gen_sctp.xml
@@ -496,9 +496,11 @@
496 496
       orthogonal to the sets of TCP, UDP and generic INET options:
497 497
       only those options which are explicitly listed below are allowed
498 498
       for SCTP sockets. Options can be set on the socket using
499  
-      <c>gen_sctp:open/1,2</c> or <c>inet:setopts/2</c>,
500  
-      retrieved using <c>inet:getopts/2</c>, and when calling
501  
-      <c>gen_sctp:connect/4,5</c> options can be changed.</p>
  499
+      <seealso marker="#open/1"><c>gen_sctp:open/1,2</c></seealso>
  500
+      or <seealso marker="inet#setopts/2"><c>inet:setopts/2</c></seealso>,
  501
+      retrieved using <seealso marker="inet#getopts/2"><c>inet:getopts/2</c></seealso>,
  502
+      and when calling <seealso marker="#connect/4"><c>gen_sctp:connect/4,5</c></seealso>
  503
+      options can be changed.</p>
502 504
     <marker id="option-binary"></marker>
503 505
     <marker id="option-list"></marker>
504 506
     <taglist>
@@ -507,7 +509,7 @@
507 509
         <p>Determines the type of data returned from <c>gen_sctp:recv/1,2</c>.</p>
508 510
         <marker id="option-active"></marker>
509 511
       </item>
510  
-      <tag><c>{active, true|false|once}</c></tag>
  512
+      <tag><c>{active, true|false|once|N}</c></tag>
511 513
       <item>
512 514
         <list type="bulleted">
513 515
           <item>
@@ -524,11 +526,28 @@
524 526
           </item>
525 527
           <item>
526 528
             <p>If <c>once</c>, only one message is automatically placed
527  
-              in the message queue, after that the mode is automatically
528  
-              re-set to passive. This provides flow control as well as
  529
+              in the message queue, and after that the mode is automatically
  530
+              reset to passive. This provides flow control as well as
529 531
               the possibility for the receiver to listen for its incoming
530 532
               SCTP data interleaved with other inter-process messages.</p>
531 533
           </item>
  534
+          <item>
  535
+            <p>If <c>active</c> is specified as an integer <c>N</c> in the
  536
+              range -32768 to 32767 (inclusive), then that number is added to
  537
+              the socket's count of the number of data messages to be
  538
+              delivered to the controlling process. If the result of the
  539
+              addition would be negative, the count is set to 0. Once the
  540
+              count reaches 0, either through the delivery of messages or by
  541
+              being explicitly set with <seealso
  542
+              marker="inet#setopts/2">inet:setopts/2</seealso>, the socket's
  543
+              mode is automatically reset to passive (<c>{active,
  544
+              false}</c>) mode. When a socket in this active mode transitions to
  545
+              passive mode, the message <c>{sctp_passive, Socket}</c> is sent
  546
+              to the controlling process to notify it that if it wants to
  547
+              receive more data messages from the socket, it must call
  548
+              <seealso marker="inet#setopts/2">inet:setopts/2</seealso> to set
  549
+              the socket back into an active mode.</p>
  550
+          </item>
532 551
         </list>
533 552
       </item>
534 553
      <tag><c>{tos, integer()}</c></tag>
6  lib/kernel/doc/src/gen_tcp.xml
@@ -148,6 +148,12 @@ do_recv(Sock, Bs) ->
148 148
           as messages:</p>
149 149
         <code type="none">
150 150
 {tcp, Socket, Data}</code>
  151
+        <p>If the socket is in <c>{active, N}</c> mode (see <seealso marker="inet#setopts/2">
  152
+          inet:setopts/2</seealso> for details) and its message counter
  153
+          drops to 0, the following message is delivered to indicate that the
  154
+          socket has transitioned to passive (<c>{active, false}</c>) mode:</p>
  155
+        <code type="none">
  156
+{tcp_passive, Socket}</code>
151 157
         <p>If the socket is closed, the following message is delivered:</p>
152 158
         <code type="none">
153 159
 {tcp_closed, Socket}</code>
17  lib/kernel/doc/src/gen_udp.xml
@@ -145,14 +145,23 @@
145 145
               <seealso marker="inet#setopts/2">inet:setopts/2</seealso>.</p>
146 146
           </item>
147 147
         </taglist>
148  
-        <p>The returned socket <c><anno>Socket</anno></c> is used to send packets
149  
-          from this port with <c>send/4</c>. When UDP packets arrive at
150  
-          the opened port, they are delivered as messages:</p>
  148
+        <p>The returned socket <c><anno>Socket</anno></c> is used to send
  149
+          packets from this port with <c>send/4</c>. When UDP packets arrive
  150
+          at the opened port, if the socket is in an active mode the packets
  151
+          are delivered as messages to the controlling process:</p>
151 152
         <code type="none">
152 153
 {udp, Socket, IP, InPortNo, Packet}</code>
153  
-        <p>Note that arriving UDP packets that are longer than
  154
+        <p>If the socket is not in an active mode, data can be
  155
+          retrieved via the <seealso marker="#recv/2">recv/2,3</seealso> calls.
  156
+          Note that arriving UDP packets that are longer than
154 157
           the receive buffer option specifies, might be truncated
155 158
           without warning.</p>
  159
+        <p>When a socket in <c>{active, N}</c> mode (see <seealso marker="inet#setopts/2">
  160
+          inet:setopts/2</seealso> for details) transitions to passive
  161
+          (<c>{active, false}</c>) mode, the controlling process is notified by a
  162
+          message of the following form:</p>
  163
+        <code type="none">
  164
+{udp_passive, Socket}</code>
156 165
         <p><c>IP</c> and <c>InPortNo</c> define the address from which
157 166
           <c>Packet</c> came. <c>Packet</c> is a list of bytes if
158 167
           the option <c>list</c> was specified. <c>Packet</c> is a
51  lib/kernel/doc/src/inet.xml
@@ -456,47 +456,66 @@ fe80::204:acff:fe17:bf38
456 456
         <p>Sets one or more options for a socket. The following options
457 457
           are available:</p>
458 458
         <taglist>
459  
-          <tag><c>{active, true | false | once}</c></tag>
  459
+          <tag><c>{active, true | false | once | N}</c></tag>
460 460
           <item>
461 461
             <p>If the value is <c>true</c>, which is the default,
462 462
               everything received from the socket will be sent as
463 463
               messages to the receiving process. If the value is
464 464
               <c>false</c> (passive mode), the process must explicitly
465  
-              receive incoming data by calling <c>gen_tcp:recv/2,3</c>
466  
-              or <c>gen_udp:recv/2,3</c> (depending on the type of
467  
-              socket).</p>
  465
+              receive incoming data by calling
  466
+              <seealso marker="gen_tcp#recv/2"><c>gen_tcp:recv/2,3</c></seealso>,
  467
+              <seealso marker="gen_udp#recv/2"><c>gen_udp:recv/2,3</c></seealso>
  468
+              or <seealso marker="gen_sctp#recv/1"><c>gen_sctp:recv/1,2</c></seealso>
  469
+              (depending on the type of socket).</p>
468 470
             <p>If the value is <c>once</c> (<c>{active, once}</c>),
469 471
               <em>one</em> data message from the socket will be sent
470 472
               to the process.  To receive one more message,
471 473
               <c>setopts/2</c> must be called again with the
472 474
               <c>{active, once}</c> option.</p>
473  
-            <p>When using <c>{active, once}</c>, the socket changes
474  
-              behaviour automatically when data is received. This can
475  
-              sometimes be confusing in combination with connection
476  
-              oriented sockets (i.e. <c>gen_tcp</c>) as a socket with
477  
-              <c>{active, false}</c> behaviour reports closing
  475
+            <p>If the value is an integer <c>N</c> in the range -32768 to 32767
  476
+              (inclusive), the value is added to the socket's count of data
  477
+              messages sent to the controlling process. A socket's default
  478
+              message count is 0. If a negative value is specified and its
  479
+              magnitude is equal to or greater than the socket's current
  480
+              message count, the socket's message count is set to 0. Once
  481
+              the socket's message count reaches 0, either due to sending
  482
+              received data messages to the process or by being explicitly set,
  483
+              the process is then notified by a special message, specific to
  484
+              the type of socket, that the socket has entered passive
  485
+              mode. Once the socket enters passive mode, to receive more
  486
+              messages <c>setopts/2</c> must be called again to set the
  487
+              socket back into an active mode.</p>
  488
+            <p>When using <c>{active, once}</c> or <c>{active, N}</c>, the
  489
+              socket changes behaviour automatically when data is received.
  490
+              This can sometimes be confusing in combination with
  491
+              connection-oriented sockets (i.e. <c>gen_tcp</c>) as a socket
  492
+              with <c>{active, false}</c> behaviour reports closing
478 493
               differently than a socket with <c>{active, true}</c>
479 494
               behaviour. To make programming easier, a socket where
480 495
               the peer closed and this was detected while in
481 496
               <c>{active, false}</c> mode, will still generate the
482 497
               message
483  
-              <c>{tcp_closed,Socket}</c> when set to <c>{active, once}</c> or <c>{active, true}</c> mode. It is therefore
  498
+              <c>{tcp_closed,Socket}</c> when set to <c>{active, once}</c>,
  499
+              <c>{active, true}</c> or <c>{active, N}</c> mode. It is therefore
484 500
               safe to assume that the message
485 501
               <c>{tcp_closed,Socket}</c>, possibly followed by socket
486 502
               port termination (depending on the <c>exit_on_close</c>
487 503
               option) will eventually appear when a socket changes
488 504
               back and forth between <c>{active, true}</c> and
489  
-              <c>{active, false}</c> mode. However, 
  505
+              <c>{active, false}</c> mode. However,
490 506
               <em>when</em> peer closing is detected is all up to the
491 507
               underlying TCP/IP stack and protocol.</p>
492  
-            <p>Note that <c>{active,true}</c> mode provides no flow
  508
+            <p>Note that <c>{active, true}</c> mode provides no flow
493 509
               control; a fast sender could easily overflow the
494  
-              receiver with incoming messages. Use active mode only if
  510
+              receiver with incoming messages. The same is true of
  511
+              <c>{active, N}</c> mode while the message count is greater
  512
+              than zero. Use active mode only if
495 513
               your high-level protocol provides its own flow control
496 514
               (for instance, acknowledging received messages) or the
497  
-              amount of data exchanged is small. <c>{active,false}</c>
498  
-              mode or use of the <c>{active, once}</c> mode provides
499  
-              flow control; the other side will not be able send
  515
+              amount of data exchanged is small. <c>{active, false}</c>
  516
+              mode, use of the <c>{active, once}</c> mode or <c>{active, N}</c>
  517
+              mode with values of <c>N</c> appropriate for the application
  518
+              provides flow control; the other side will not be able send
500 519
               faster than the receiver can read.</p>
501 520
           </item>
502 521
 
2  lib/kernel/src/gen_sctp.erl
@@ -36,7 +36,7 @@
36 36
 
37 37
 -type assoc_id() :: term().
38 38
 -type option() ::
39  
-        {active, true | false | once} |
  39
+        {active, true | false | once | -32768..32767} |
40 40
         {buffer, non_neg_integer()} |
41 41
         {dontroute, boolean()} |
42 42
         {high_msgq_watermark, pos_integer()} |
2  lib/kernel/src/gen_tcp.erl
@@ -30,7 +30,7 @@
30 30
 -include("file.hrl").
31 31
 
32 32
 -type option() ::
33  
-        {active,          true | false | once} |
  33
+        {active,          true | false | once | -32768..32767} |
34 34
         {buffer,          non_neg_integer()} |
35 35
         {delay_send,      boolean()} |
36 36
         {deliver,         port | term} |
2  lib/kernel/src/gen_udp.erl
@@ -26,7 +26,7 @@
26 26
 -include("inet_int.hrl").
27 27
 
28 28
 -type option() ::
29  
-        {active,          true | false | once} |
  29
+        {active,          true | false | once | -32768..32767} |
30 30
         {add_membership,  {inet:ip_address(), inet:ip_address()}} |
31 31
         {broadcast,       boolean()} |
32 32
         {buffer,          non_neg_integer()} |
14  lib/kernel/src/inet.erl
@@ -667,6 +667,9 @@ con_opt([Opt | Opts], R, As) ->
667 667
 		false ->
668 668
 		    {error, badarg}
669 669
 	    end;
  670
+        {active,N} when is_integer(N), N < 32768, N >= -32768 ->
  671
+            NOpts = lists:keydelete(active, 1, R#connect_opts.opts),
  672
+            con_opt(Opts, R#connect_opts { opts = [{active,N}|NOpts] }, As);
670 673
 	{Name,Val} when is_atom(Name) -> con_add(Name, Val, R, Opts, As);
671 674
 	_ -> {error, badarg}
672 675
     end;
@@ -733,6 +736,9 @@ list_opt([Opt | Opts], R, As) ->
733 736
 		false ->
734 737
 		    {error, badarg}
735 738
 	    end;
  739
+        {active,N} when is_integer(N), N < 32768, N >= -32768 ->
  740
+            NOpts = lists:keydelete(active, 1, R#listen_opts.opts),
  741
+            list_opt(Opts, R#listen_opts { opts = [{active,N}|NOpts] }, As);
736 742
 	{Name,Val} when is_atom(Name) -> list_add(Name, Val, R, Opts, As);
737 743
 	_ -> {error, badarg}
738 744
     end;
@@ -787,6 +793,9 @@ udp_opt([Opt | Opts], R, As) ->
787 793
 		false ->
788 794
 		    {error, badarg}
789 795
 	    end;
  796
+        {active,N} when is_integer(N), N < 32768, N >= -32768 ->
  797
+            NOpts = lists:keydelete(active, 1, R#udp_opts.opts),
  798
+            udp_opt(Opts, R#udp_opts { opts = [{active,N}|NOpts] }, As);
790 799
 	{Name,Val} when is_atom(Name) -> udp_add(Name, Val, R, Opts, As);
791 800
 	_ -> {error, badarg}
792 801
     end;
@@ -805,7 +814,7 @@ udp_add(Name, Val, R, Opts, As) ->
805 814
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
806 815
 %  Currently supported options include:
807 816
 %  (*) {mode,   list|binary}	 or just list|binary
808  
-%  (*) {active, true|false|once}
  817
+%  (*) {active, true|false|once|N}
809 818
 %  (*) {sctp_module, inet_sctp|inet6_sctp} or just inet|inet6
810 819
 %  (*) options set via setsockopt.
811 820
 %      The full list is below in sctp_options/0 .
@@ -867,6 +876,9 @@ sctp_opt([Opt|Opts], Mod, R, As) ->
867 876
 		false ->
868 877
 		    {error, badarg}
869 878
 	    end;
  879
+        {active,N} when is_integer(N), N < 32768, N >= -32768 ->
  880
+            NOpts = lists:keydelete(active, 1, R#sctp_opts.opts),
  881
+            sctp_opt(Opts, Mod, R#sctp_opts { opts = [{active,N}|NOpts] }, As);
870 882
 	{Name,Val}	-> sctp_opt (Opts, Mod, R, As, Name, Val);
871 883
 	_ -> {error,badarg}
872 884
     end;
1  lib/kernel/src/inet_int.hrl
@@ -46,6 +46,7 @@
46 46
 -define(INET_PASSIVE, 0).
47 47
 -define(INET_ACTIVE,  1).
48 48
 -define(INET_ONCE,    2). % Active once then passive
  49
+-define(INET_MULTI,   3). % Active N then passive
49 50
 
50 51
 %% state codes (getstatus, INET_REQ_GETSTATUS)
51 52
 -define(INET_F_OPEN,         16#0001).
125  lib/kernel/test/gen_sctp_SUITE.erl
@@ -35,8 +35,9 @@
35 35
     open_unihoming_ipv6_socket/1,
36 36
     open_multihoming_ipv6_socket/1,
37 37
     open_multihoming_ipv4_and_ipv6_socket/1,
38  
-    basic_stream/1, xfer_stream_min/1, peeloff_active_once/1,
39  
-    peeloff_active_true/1, buffers/1]).
  38
+    basic_stream/1, xfer_stream_min/1, active_n/1,
  39
+    peeloff_active_once/1, peeloff_active_true/1, peeloff_active_n/1,
  40
+    buffers/1]).
40 41
 
41 42
 suite() -> [{ct_hooks,[ts_install_cth]}].
42 43
 
@@ -46,9 +47,9 @@ all() ->
46 47
      open_multihoming_ipv4_socket,
47 48
      open_unihoming_ipv6_socket,
48 49
      open_multihoming_ipv6_socket,
49  
-     open_multihoming_ipv4_and_ipv6_socket,
  50
+     open_multihoming_ipv4_and_ipv6_socket, active_n,
50 51
      basic_stream, xfer_stream_min, peeloff_active_once,
51  
-     peeloff_active_true, buffers].
  52
+     peeloff_active_true, peeloff_active_n, buffers].
52 53
 
53 54
 groups() -> 
54 55
     [].
@@ -767,6 +768,106 @@ implicit_inet6(S1, Addr) ->
767 768
 	  end,
768 769
     ?line ok = gen_sctp:close(S2).
769 770
 
  771
+active_n(doc) ->
  772
+    "Verify {active,N} socket management";
  773
+active_n(suite) ->
  774
+    [];
  775
+active_n(Config) when is_list(Config) ->
  776
+    N = 3,
  777
+    S1 = ok(gen_sctp:open([{active,N}])),
  778
+    [{active,N}] = ok(inet:getopts(S1, [active])),
  779
+    ok = inet:setopts(S1, [{active,-N}]),
  780
+    receive
  781
+        {sctp_passive, S1} -> ok
  782
+    after
  783
+        5000 ->
  784
+            exit({error,sctp_passive_failure})
  785
+    end,
  786
+    [{active,false}] = ok(inet:getopts(S1, [active])),
  787
+    ok = inet:setopts(S1, [{active,0}]),
  788
+    receive
  789
+        {sctp_passive, S1} -> ok
  790
+    after
  791
+        5000 ->
  792
+            exit({error,sctp_passive_failure})
  793
+    end,
  794
+    ok = inet:setopts(S1, [{active,32767}]),
  795
+    {error,einval} = inet:setopts(S1, [{active,1}]),
  796
+    {error,einval} = inet:setopts(S1, [{active,-32769}]),
  797
+    ok = inet:setopts(S1, [{active,-32768}]),
  798
+    receive
  799
+        {sctp_passive, S1} -> ok
  800
+    after
  801
+        5000 ->
  802
+            exit({error,sctp_passive_failure})
  803
+    end,
  804
+    [{active,false}] = ok(inet:getopts(S1, [active])),
  805
+    ok = inet:setopts(S1, [{active,N}]),
  806
+    ok = inet:setopts(S1, [{active,true}]),
  807
+    [{active,true}] = ok(inet:getopts(S1, [active])),
  808
+    receive
  809
+        _ -> exit({error,active_n})
  810
+    after
  811
+        0 ->
  812
+            ok
  813
+    end,
  814
+    ok = inet:setopts(S1, [{active,N}]),
  815
+    ok = inet:setopts(S1, [{active,once}]),
  816
+    [{active,once}] = ok(inet:getopts(S1, [active])),
  817
+    receive
  818
+        _ -> exit({error,active_n})
  819
+    after
  820
+        0 ->
  821
+            ok
  822
+    end,
  823
+    {error,einval} = inet:setopts(S1, [{active,32768}]),
  824
+    ok = inet:setopts(S1, [{active,false}]),
  825
+    [{active,false}] = ok(inet:getopts(S1, [active])),
  826
+    ok = gen_sctp:listen(S1, true),
  827
+    S1Port = ok(inet:port(S1)),
  828
+    S2 = ok(gen_sctp:open(0, [{active,false}])),
  829
+    Assoc = ok(gen_sctp:connect(S2, "localhost", S1Port, [])),
  830
+    ok = inet:setopts(S1, [{active,N}]),
  831
+    [{active,N}] = ok(inet:getopts(S1, [active])),
  832
+    LoopFun = fun(Count, Count, _Fn) ->
  833
+		      receive
  834
+			  {sctp_passive,S1} ->
  835
+			      ok
  836
+		      after
  837
+			  5000 ->
  838
+			      exit({error,timeout})
  839
+		      end;
  840
+		 (I, Count, Fn) ->
  841
+		      Msg = list_to_binary("message "++integer_to_list(I)),
  842
+		      ok = gen_sctp:send(S2, Assoc, 0, Msg),
  843
+		      receive
  844
+			  {sctp,S1,_,_,{[SR],Msg}} when is_record(SR, sctp_sndrcvinfo) ->
  845
+			      Fn(I+1, Count, Fn);
  846
+			  {sctp,S1,_,_,_} ->
  847
+			      %% ignore non-data messages
  848
+			      ok = inet:setopts(S1, [{active,1}]),
  849
+			      Fn(I, Count, Fn);
  850
+			  Other ->
  851
+			      exit({unexpected, Other})
  852
+		      after
  853
+			  5000 ->
  854
+			      exit({error,timeout})
  855
+		      end
  856
+	      end,
  857
+    ok = LoopFun(1, N, LoopFun),
  858
+    S3 = ok(gen_sctp:open([{active,0}])),
  859
+    receive
  860
+        {sctp_passive,S3} ->
  861
+            [{active,false}] = ok(inet:getopts(S3, [active]))
  862
+    after
  863
+        5000 ->
  864
+            exit({error,udp_passive})
  865
+    end,
  866
+    ok = gen_sctp:close(S3),
  867
+    ok = gen_sctp:close(S2),
  868
+    ok = gen_sctp:close(S1),
  869
+    ok.
  870
+
770 871
 basic_stream(doc) ->
771 872
     "Hello world stream socket";
772 873
 basic_stream(suite) ->
@@ -941,6 +1042,14 @@ peeloff_active_true(suite) ->
941 1042
 peeloff_active_true(Config) ->
942 1043
     peeloff(Config, [{active,true}]).
943 1044
 
  1045
+peeloff_active_n(doc) ->
  1046
+    "Peel off an SCTP stream socket ({active,N})";
  1047
+peeloff_active_n(suite) ->
  1048
+    [];
  1049
+
  1050
+peeloff_active_n(Config) ->
  1051
+    peeloff(Config, [{active,1}]).
  1052
+
944 1053
 peeloff(Config, SockOpts) when is_list(Config) ->
945 1054
     ?line Addr = {127,0,0,1},
946 1055
     ?line Stream = 0,
@@ -1519,7 +1628,13 @@ s_loop(Socket, Timeout, Parent, Handler, State) ->
1519 1628
     end.
1520 1629
 
1521 1630
 again(Socket) ->
1522  
-    inet:setopts(Socket, [{active,once}]).
  1631
+    receive
  1632
+	{sctp_passive,Socket} ->
  1633
+	    [{active, false}] = ok(inet:getopts(Socket, [active])),
  1634
+	    ok = inet:setopts(Socket,[{active,1}])
  1635
+    after 0 ->
  1636
+	    ok = inet:setopts(Socket, [{active,once}])
  1637
+    end.
1523 1638
 
1524 1639
 gb_push(Key, Val, GBT) ->
1525 1640
     case gb_trees:lookup(Key, GBT) of
114  lib/kernel/test/gen_tcp_misc_SUITE.erl
@@ -25,7 +25,7 @@
25 25
 -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, 
26 26
 	 init_per_group/2,end_per_group/2, 
27 27
 	 controlling_process/1, controlling_process_self/1,
28  
-	 no_accept/1, close_with_pending_output/1,
  28
+	 no_accept/1, close_with_pending_output/1, active_n/1,
29 29
 	 data_before_close/1, iter_max_socks/1, get_status/1,
30 30
 	 passive_sockets/1, accept_closed_by_other_process/1,
31 31
 	 init_per_testcase/2, end_per_testcase/2,
@@ -70,7 +70,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
70 70
 all() -> 
71 71
     [controlling_process, controlling_process_self, no_accept,
72 72
      close_with_pending_output, data_before_close,
73  
-     iter_max_socks, passive_sockets,
  73
+     iter_max_socks, passive_sockets, active_n,
74 74
      accept_closed_by_other_process, otp_3924, closed_socket,
75 75
      shutdown_active, shutdown_passive, shutdown_pending,
76 76
      default_options, http_bad_packet, busy_send,
@@ -407,6 +407,114 @@ send_loop(Sock, Data, Left) ->
407 407
     ok = gen_tcp:send(Sock, Data),
408 408
     send_loop(Sock, Data, Left-1).
409 409
 
  410
+%% Test {active,N} option
  411
+active_n(doc) ->
  412
+    ["Verify operation of the {active,N} option."];
  413
+active_n(suite) -> [];
  414
+active_n(Config) when is_list(Config) ->
  415
+    N = 3,
  416
+    LS = ok(gen_tcp:listen(0, [{active,N}])),
  417
+    [{active,N}] = ok(inet:getopts(LS, [active])),
  418
+    ok = inet:setopts(LS, [{active,-N}]),
  419
+    receive
  420
+        {tcp_passive, LS} -> ok
  421
+    after
  422
+        5000 ->
  423
+            exit({error,tcp_passive_failure})
  424
+    end,
  425
+    [{active,false}] = ok(inet:getopts(LS, [active])),
  426
+    ok = inet:setopts(LS, [{active,0}]),
  427
+    receive
  428
+        {tcp_passive, LS} -> ok
  429
+    after
  430
+        5000 ->
  431
+            exit({error,tcp_passive_failure})
  432
+    end,
  433
+    ok = inet:setopts(LS, [{active,32767}]),
  434
+    {error,einval} = inet:setopts(LS, [{active,1}]),
  435
+    {error,einval} = inet:setopts(LS, [{active,-32769}]),
  436
+    ok = inet:setopts(LS, [{active,-32768}]),
  437
+    receive
  438
+        {tcp_passive, LS} -> ok
  439
+    after
  440
+        5000 ->
  441
+            exit({error,tcp_passive_failure})
  442
+    end,
  443
+    [{active,false}] = ok(inet:getopts(LS, [active])),
  444
+    ok = inet:setopts(LS, [{active,N}]),
  445
+    ok = inet:setopts(LS, [{active,true}]),
  446
+    [{active,true}] = ok(inet:getopts(LS, [active])),
  447
+    receive
  448
+        _ -> exit({error,active_n})
  449
+    after
  450
+        0 ->
  451
+            ok
  452
+    end,
  453
+    ok = inet:setopts(LS, [{active,N}]),
  454
+    ok = inet:setopts(LS, [{active,once}]),
  455
+    [{active,once}] = ok(inet:getopts(LS, [active])),
  456
+    receive
  457
+        _ -> exit({error,active_n})
  458
+    after
  459
+        0 ->
  460
+            ok
  461
+    end,
  462
+    {error,einval} = inet:setopts(LS, [{active,32768}]),
  463
+    ok = inet:setopts(LS, [{active,false}]),
  464
+    [{active,false}] = ok(inet:getopts(LS, [active])),
  465
+    Port = ok(inet:port(LS)),
  466
+    C = ok(gen_tcp:connect("localhost", Port, [{active,N}])),
  467
+    [{active,N}] = ok(inet:getopts(C, [active])),
  468
+    S = ok(gen_tcp:accept(LS)),
  469
+    ok = inet:setopts(S, [{active,N}]),
  470
+    [{active,N}] = ok(inet:getopts(S, [active])),
  471
+    repeat(3,
  472
+           fun(I) ->
  473
+                   Msg = "message "++integer_to_list(I),
  474
+                   ok = gen_tcp:send(C, Msg),
  475
+                   receive
  476
+                       {tcp,S,Msg} ->
  477
+                           ok = gen_tcp:send(S, Msg)
  478
+                   after
  479
+                       5000 ->
  480
+                           exit({error,timeout})
  481
+                   end,
  482
+                   receive
  483
+                       {tcp,C,Msg} ->
  484
+                           ok
  485
+                   after
  486
+                       5000 ->
  487
+                           exit({error,timeout})
  488
+                   end
  489
+           end),
  490
+    receive
  491
+        {tcp_passive,S} ->
  492
+            [{active,false}] = ok(inet:getopts(S, [active]))
  493
+    after
  494
+        5000 ->
  495
+            exit({error,tcp_passive})
  496
+    end,
  497
+    receive
  498
+        {tcp_passive,C} ->
  499
+            [{active,false}] = ok(inet:getopts(C, [active]))
  500
+    after
  501
+        5000 ->
  502
+            exit({error,tcp_passive})
  503
+    end,
  504
+    LS2 = ok(gen_tcp:listen(0, [{active,0}])),
  505
+    receive
  506
+        {tcp_passive,LS2} ->
  507
+            [{active,false}] = ok(inet:getopts(LS2, [active]))
  508
+    after
  509
+        5000 ->
  510
+            exit({error,tcp_passive})
  511
+    end,
  512
+    ok = gen_tcp:close(LS2),
  513
+    ok = gen_tcp:close(C),
  514
+    ok = gen_tcp:close(S),
  515
+    ok = gen_tcp:close(LS),
  516
+    ok.
  517
+
410 518
 -define(OTP_3924_MAX_DELAY, 100).
411 519
 %% Taken out of the blue, but on intra host connections
412 520
 %% I expect propagation of a close to be quite fast
@@ -2659,3 +2767,5 @@ oct_aloop(S,X,Times) ->
2659 2767
 	    gen_tcp:close(S),
2660 2768
 	    closed
2661 2769
     end.
  2770
+
  2771
+ok({ok,V}) -> V.
106  lib/kernel/test/gen_udp_SUITE.erl
@@ -34,7 +34,7 @@
34 34
 	 init_per_group/2,end_per_group/2]).
35 35
 -export([init_per_testcase/2, end_per_testcase/2]).
36 36
 
37  
--export([send_to_closed/1, 
  37
+-export([send_to_closed/1, active_n/1,
38 38
 	 buffer_size/1, binary_passive_recv/1, bad_address/1,
39 39
 	 read_packets/1, open_fd/1, connect/1, implicit_inet6/1]).
40 40
 
@@ -43,7 +43,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
43 43
 all() -> 
44 44
     [send_to_closed, buffer_size, binary_passive_recv,
45 45
      bad_address, read_packets, open_fd, connect,
46  
-     implicit_inet6].
  46
+     implicit_inet6, active_n].
47 47
 
48 48
 groups() -> 
49 49
     [].
@@ -466,6 +466,108 @@ open_fd(Config) when is_list(Config) ->
466 466
 	    ?t:fail(io_lib:format("~w", [flush()]))
467 467
     end.
468 468
 
  469
+active_n(Config) when is_list(Config) ->
  470
+    N = 3,
  471
+    S1 = ok(gen_udp:open(0, [{active,N}])),
  472
+    [{active,N}] = ok(inet:getopts(S1, [active])),
  473
+    ok = inet:setopts(S1, [{active,-N}]),
  474
+    receive
  475
+        {udp_passive, S1} -> ok
  476
+    after
  477
+        5000 ->
  478
+            exit({error,udp_passive_failure})
  479
+    end,
  480
+    [{active,false}] = ok(inet:getopts(S1, [active])),
  481
+    ok = inet:setopts(S1, [{active,0}]),
  482
+    receive
  483
+        {udp_passive, S1} -> ok
  484
+    after
  485
+        5000 ->
  486
+            exit({error,udp_passive_failure})
  487
+    end,
  488
+    ok = inet:setopts(S1, [{active,32767}]),
  489
+    {error,einval} = inet:setopts(S1, [{active,1}]),
  490
+    {error,einval} = inet:setopts(S1, [{active,-32769}]),
  491
+    ok = inet:setopts(S1, [{active,-32768}]),
  492
+    receive
  493
+        {udp_passive, S1} -> ok
  494
+    after
  495
+        5000 ->
  496
+            exit({error,udp_passive_failure})
  497
+    end,
  498
+    [{active,false}] = ok(inet:getopts(S1, [active])),
  499
+    ok = inet:setopts(S1, [{active,N}]),
  500
+    ok = inet:setopts(S1, [{active,true}]),
  501
+    [{active,true}] = ok(inet:getopts(S1, [active])),
  502
+    receive
  503
+        _ -> exit({error,active_n})
  504
+    after
  505
+        0 ->
  506
+            ok
  507
+    end,
  508
+    ok = inet:setopts(S1, [{active,N}]),
  509
+    ok = inet:setopts(S1, [{active,once}]),
  510
+    [{active,once}] = ok(inet:getopts(S1, [active])),
  511
+    receive
  512
+        _ -> exit({error,active_n})
  513
+    after
  514
+        0 ->
  515
+            ok
  516
+    end,
  517
+    {error,einval} = inet:setopts(S1, [{active,32768}]),
  518
+    ok = inet:setopts(S1, [{active,false}]),
  519
+    [{active,false}] = ok(inet:getopts(S1, [active])),
  520
+    S1Port = ok(inet:port(S1)),
  521
+    S2 = ok(gen_udp:open(0, [{active,N}])),
  522
+    S2Port = ok(inet:port(S2)),
  523
+    [{active,N}] = ok(inet:getopts(S2, [active])),
  524
+    ok = inet:setopts(S1, [{active,N}]),
  525
+    [{active,N}] = ok(inet:getopts(S1, [active])),
  526
+    lists:foreach(
  527
+      fun(I) ->
  528
+              Msg = "message "++integer_to_list(I),
  529
+              ok = gen_udp:send(S2, "localhost", S1Port, Msg),
  530
+              receive
  531
+                  {udp,S1,_,S2Port,Msg} ->
  532
+                      ok = gen_udp:send(S1, "localhost", S2Port, Msg)
  533
+              after
  534
+                  5000 ->
  535
+                      exit({error,timeout})
  536
+              end,
  537
+              receive
  538
+                  {udp,S2,_,S1Port,Msg} ->
  539
+                      ok
  540
+              after
  541
+                  5000 ->
  542
+                      exit({error,timeout})
  543
+              end
  544
+      end, lists:seq(1,N)),
  545
+    receive
  546
+        {udp_passive,S1} ->
  547
+            [{active,false}] = ok(inet:getopts(S1, [active]))
  548
+    after
  549
+        5000 ->
  550
+            exit({error,udp_passive})
  551
+    end,
  552
+    receive
  553
+        {udp_passive,S2} ->
  554
+            [{active,false}] = ok(inet:getopts(S2, [active]))
  555
+    after
  556
+        5000 ->
  557
+            exit({error,udp_passive})
  558
+    end,
  559
+    S3 = ok(gen_udp:open(0, [{active,0}])),
  560
+    receive
  561
+        {udp_passive,S3} ->
  562
+            [{active,false}] = ok(inet:getopts(S3, [active]))
  563
+    after
  564
+        5000 ->
  565
+            exit({error,udp_passive})
  566
+    end,
  567
+    ok = gen_udp:close(S3),
  568
+    ok = gen_udp:close(S2),
  569
+    ok = gen_udp:close(S1),
  570
+    ok.
469 571
 
470 572
 %
471 573
 % Utils

No commit comments for this range

Something went wrong with that request. Please try again.