anchor.diff:      exit({'client-ctrl', Tag, Reason}).
   
   
  -start_client(#{domain   := Domain,
  -	       type     := Type,
  -	       protocol := Proto,
  -	       server   := #{addr := Addr, port := Port},
  -	       cid      := ID,
  -	       clients  := Clients0} = State, Backend) ->
  +start_gen_client(#{domain   := Domain,
  +		   type     := Type,
  +		   protocol := Proto,
  +		   server   := #{addr := Addr, port := Port},
  +		   cid      := ID,
  +		   clients  := Clients0} = State, Backend) ->
       Self = self(),
  -    i("try start client ~w", [ID]),
  +    i("try start (gen) client ~w", [ID]),
       LifeTime = rand:uniform(timer:minutes(3)) + timer:minutes(2),
       {Pid, MRef} =
   	spawn_monitor(fun() ->
  -			      client_start(Self,
  -					   Backend, ID,
  -					   LifeTime,
  -					   Domain, Type, Proto,
  -					   Addr, Port)
  +			      gen_client_start(Self,
  +					       Backend, ID,
  +					       LifeTime,
  +					       Domain, Type, Proto,
  +					       Addr, Port)
   		      end),
       receive
   	{'DOWN', MRef, process, Pid, Reason} ->
  
positive.code: 	{'$socket', Sock, select, Info} ->
  	    case socket:recv(Sock) of
  		{ok, Data} ->
  		    handler_process_data(State, Data);
  		{error, Reason} ->
  		    i("recv failed: "
  		      "~n   ~p", [Reason]),
  
negative.code:     end.
  
  
  esock_client_start(Parent, ID,
  		   LifeTime,
  		   Domain, Type, Proto,
  		   ServerAddr, ServerPort)
    when (Type =:= stream) andalso (Proto =:= tcp) ->
      put(sname, f("esock-client[~w]", [ID])),
      %% put(debug, true),
      i("starting"),
      State = esock_client_connect(#{id          => ID,
  				   domain      => Domain,
  				   type        => Type,
  				   protocol    => Proto,
  				   server_addr => ServerAddr,
  				   server_port => ServerPort}),
      erlang:send_after(LifeTime, self(), terminate),
      MRef = erlang:monitor(process, Parent),
      Parent ! {self(), ready},
      i("started"),
      esock_client_loop(State#{condition => send_request,
  			     select    => undefined,
  			     mid       => 1,
  			     parent    => Parent,
  			     mref      => MRef,
  			     buf       => <<>>}).
  
  
  esock_client_connect(#{domain      := Domain,
  		       type        := Type,
  		       protocol    := Proto,
  		       server_addr := ServerAddr,
  		       server_port := ServerPort} = State)
    when (Type =:= stream) andalso (Proto =:= tcp) ->
      i("try open socket"),
      Sock =
  	case socket:open(Domain, Type, Proto) of
  	    {ok, S} ->
  		i("opened"),
  		S;
  	    {error, Reason1} ->
  		e("failed open: "
  		  "~n   ~p", [Reason1]),
  		client_exit(State, open, Reason1)
  	end,
      %% We are on the same machine (as the server), so just reuse that address
      i("try bind to ~s", [inet_parse:ntoa(ServerAddr)]),
      case socket:bind(Sock, #{family => Domain, addr => ServerAddr}) of
  	ok ->
  	    i("bound"),
  	    ok;
  	{error, Reason2} ->
  	    e("failed bind: "
  	      "~n   ~p", [Reason2]),
  	    (catch socket:close(Sock)),
  	    client_exit(State, bind, Reason2)
      end,
      i("try connect to ~s:~w", [inet_parse:ntoa(ServerAddr), ServerPort]),
      case socket:connect(Sock, #{family => Domain,
  				addr   => ServerAddr,
  				port   => ServerPort}) of
  	ok ->
  	    i("connected"),
  	    ?SOCKET_MONITOR !
  		{monitor, Sock,
  		 string_of(socket, Sock),
  		 sockmon_fun(socket), self()},
  	    State#{sock => Sock};
  	{error, Reason3} ->
  	    e("failed connecting: "
  	      "~n   ~p", [Reason3]),
  	    (catch socket:close(Sock)),
  	    client_exit(State, connect, Reason3)
      end.
  
  
  gen_client_loop(#{condition := terminate,
  		  sock      := Sock}) ->
      (catch gen_tcp:close(Sock)),
      exit(normal);
  gen_client_loop(#{condition := {await_reply, _MID},
  		  parent    := Parent,
  		  sock      := Sock} = State) ->
      receive
  	{'DOWN', _MRef, process, Parent, Reason} ->
  	    e("unexpected down from parent received: "
  	      "~n   ~p", [Reason]),
  	    client_exit(State, parent_down, Reason);
  
  	{tcp, Sock, Data} ->
  	    %% i("received (~w bytes of) data", [size(Data)]),
  	    gen_client_loop(client_process_data(State, Data));
  
  	terminate ->
  	    gen_client_loop(State#{condition => terminate})
  
      end;
  gen_client_loop(#{condition := send_request,
  		  sock      := Sock,
  		  mid       := MID} = State) ->
      %% i("try send request ~w", [MID]),
      Data = ?DATA,
      SZ   = size(Data),
      Req  = <<?MTAG:32, MID:32, ?REQUEST:32, SZ:32, Data/binary>>,
      case gen_tcp:send(Sock, Req) of
  	ok ->
  	    gen_client_loop(State#{condition => {await_reply, MID},
  				   mid => MID + 1});
  	{error, Reason} ->
  	    e("failed sending request ~w: "
  	      "~n   ~p", [MID, Reason]),
  	    client_exit(State, send, Reason)
      end.
  
  
  
  esock_client_loop(#{condition := terminate,
  		    sock      := Sock}) ->
      (catch socket:close(Sock)),
      exit(normal);
  esock_client_loop(#{condition := {await_reply, _MID},
  		    select    := undefined,
  		    sock      := Sock} = State) ->
      %% i("try (nowait) recv"),
      case socket:recv(Sock, 0, nowait) of
  	{ok, Data} when is_binary(Data) ->
  	    %% i("received (~w bytes of) data", [size(Data)]),
  	    esock_client_loop(client_process_data(State, Data));
  	%% This is the "old" style
  	{ok, {Data, SelectInfo}} when is_binary(Data) ->
  	    %% i("partial recv - select"),
  	    Buf0 = maps:take(buf, State),
  	    Buf2 = <<Buf0/binary, Data/binary>>,
  	    esock_client_loop(State#{buf    => Buf2,
  				     select => SelectInfo});
  	%% This is the "new" style
  	{select, {Data, SelectInfo}} when is_binary(Data) ->
  	    %% i("partial recv - select"),
  	    Buf0 = maps:take(buf, State),
  	    Buf2 = <<Buf0/binary, Data/binary>>,
  	    esock_client_loop(State#{buf    => Buf2,
  				     select => SelectInfo});
  	{select, SelectInfo} ->
  	    %% i("select"),
  	    esock_client_loop(State#{select => SelectInfo});
  	{error, Reason} ->
  	    e("recv failed: "
  	      "~n   ~p", [Reason]),
  	    (catch socket:close(Sock)),
  	    client_exit(State, recv, Reason)
      end;
  esock_client_loop(#{condition := {await_reply, _MID},
  		    select    := {select_info, _, Info} = SelectInfo,
  		    parent    := Parent,
  		    sock      := Sock} = State) ->
      receive
  	{'DOWN', _MRef, process, Parent, Reason} ->
  	    e("unexpected down from parent received: "
  	      "~n   ~p", [Reason]),
  	    (catch socket:cancel(Sock, SelectInfo)),
  	    (catch socket:close(Sock)),
  	    client_exit(State, parent_down, Reason);
  
  	{'$socket', Sock, abort, Info} ->
  	    e("received unexpected select abort: "
  	      "~n   ~p", [Info]),
  	    (catch socket:close(Sock)),
  	    client_exit(State, abort, Info);
  
  	{'$socket', Sock, select, Info} ->
  	    %% i("select message received - try recv"),
  	    case socket:recv(Sock) of
  		{ok, Data} ->
  		    %% i("recv succeed (~w bytes of data received)", [size(Data)]),
  		    esock_client_loop(
  		      client_process_data(State#{select => undefined}, Data));
  		{error, Reason} ->
  		    e("recv failed: "
  		      "~n   ~p", [Reason]),
  		    (catch socket:close(Sock)),
  		    client_exit(State, recv, Reason)
  	    end;
  
  	terminate ->
  	    esock_client_loop(State#{condition => terminate})
  
      end;
  esock_client_loop(#{condition := send_request,
  		    sock      := Sock,
  		    mid       := MID} = State) ->
      %% i("try send request ~w", [MID]),
      Data = ?DATA,
      SZ   = size(Data),
      Req  = <<?MTAG:32, MID:32, ?REQUEST:32, SZ:32, Data/binary>>,
      case socket:send(Sock, Req) of
  	ok ->
  	    esock_client_loop(State#{condition => {await_reply, MID},
  				     mid => MID + 1});
  	{error, Reason} ->
  	    e("failed sending request ~w: "
  	      "~n   ~p", [MID, Reason]),
  	    client_exit(State, send, Reason)
      end.
  
  
  
  client_process_data(#{condition := {await_reply, MID},
  