Yes, it is possible. You can know the sequence of messages relative to process A and B, but you cannot know the message sequences relative to any other two processes and A and B (meaning, you can't know what order various streams of messages will be interleaved), and this also means you don't know the relative ordering of messages from A to B, and B to B.
How to avoid this? The most straight forward way would be to have your process T spawn process G if I does not yet exist. Or always spawn a G to handle the processing job. Or handle the processing itself. Or make the socket listener not a gen_server, but rather a pure Erlang OTP process (look up proc_lib -- I find this much smoother for socket listeners) and your weird conflicts between the gen_server world and the necessity of initializing certain aspects of socket handler processes can be made to go away.
To sum up:
- The ordering problem you think exists really does exist. In practice it will almost never happen -- until the most inopportune time in production, of course.
- Socket handlers should do nothing other than handling sockets.
- "Handling sockets" means translating between Erlang messages and network messages (if its TCP they are not packets it is a stream -- be careful), and dispatching to inner gen_* processes.
- Your initialization problem can be dealt with, but it is better to eliminate the need entirely. There is nearly always a way to do this -- when there isn't, there is
proc_lib.
UPDATE
Here is what can happen if you don't flush the mailbox with a select receive searching for your magical first message.
Here we have a spamming process that will send a bajillion messages to the soon-to-be spawned gen_server:
-module(spawn_spammer).
-export([kickoff/0]).
kickoff() ->
Spammer = start(),
Catcher = spawn_catcher:start(),
{Spammer, Catcher}.
start() ->
ok = io:format("~tp ~tp: Starting up.~n", [self(), ?MODULE]),
spawn(fun() -> loop() end).
loop() ->
try
spawn_catcher ! {self(), test_spam}
catch
_:_ -> io:format("~tp ~tp: Missed.~n", [self(), ?MODULE])
end,
receive
cut_it_out ->
ok;
Unexpected ->
io:format("~tp ~tp: Unexpected message ~tp~n", [self(), ?MODULE, Unexpected]),
loop()
after 0 ->
loop()
end.
Here we have that gen_server, trying to conduct a (supposedly) safe delayed initialization with itself:
-module(spawn_catcher).
-behavior(gen_server).
-export([start/0, init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
start() ->
ok = io:format("~tp ~tp: Starting up.~n", [self(), ?MODULE]),
gen_server:start({local, ?MODULE}, ?MODULE, [], []).
init(_) ->
ok = io:format("~tp ~tp: Blank initialization.~n", [self(), ?MODULE]),
gen_server:cast(self(), get_ready),
{ok, not_ready}.
handle_call(Message, From, State) ->
ok = io:format("~tp ~tp: Unexpected call: ~tp from ~tp~n", [self(), ?MODULE, Message, From]),
{noreply, State}.
handle_cast(get_ready, not_ready) ->
ok = io:format("~tp ~tp: Getting ready~n", [self(), ?MODULE]),
{noreply, ready};
handle_cast(Message, State) ->
ok = io:format("~tp ~tp: Unexpected call: ~tp~n", [self(), ?MODULE, Message]),
{noreply, State}.
handle_info(Message, not_ready) ->
ok = io:format("~tp ~tp: DANGEROUS MESSAGE: ~tp~n", [self(), ?MODULE, Message]),
{noreply, not_ready};
handle_info({Spammer, test_spam}, ready) ->
ok = io:format("~tp ~tp: Got first proper message. Sending reply.~n", [self(), ?MODULE]),
Spammer ! cut_it_out,
{stop, normal, ready};
handle_info(Message, ready) ->
ok = io:format("~tp ~tp: Unexpected message: ~tp~n", [self(), ?MODULE, Message]),
{noreply, ready}.
terminate(_, _) -> ok.
code_change(_, State, _) -> {ok, State}.
And here is how that plays out in the shell:
1> spawn_spammer:kickoff().
<0.33.0> spawn_spammer: Starting up.
<0.33.0> spawn_catcher: Starting up.
<0.99.0> spawn_spammer: Missed.
<0.99.0> spawn_spammer: Missed.
<0.100.0> spawn_catcher: Blank initialization.
<0.100.0> spawn_catcher: DANGEROUS MESSAGE: {<0.99.0>,test_spam}
<0.100.0> spawn_catcher: DANGEROUS MESSAGE: {<0.99.0>,test_spam}
<0.100.0> spawn_catcher: DANGEROUS MESSAGE: {<0.99.0>,test_spam}
{<0.99.0>,{ok,<0.100.0>}}
<0.100.0> spawn_catcher: DANGEROUS MESSAGE: {<0.99.0>,test_spam}
<0.100.0> spawn_catcher: DANGEROUS MESSAGE: {<0.99.0>,test_spam}
<0.100.0> spawn_catcher: DANGEROUS MESSAGE: {<0.99.0>,test_spam}
<0.100.0> spawn_catcher: DANGEROUS MESSAGE: {<0.99.0>,test_spam}
<0.100.0> spawn_catcher: DANGEROUS MESSAGE: {<0.99.0>,test_spam}
<0.100.0> spawn_catcher: DANGEROUS MESSAGE: {<0.99.0>,test_spam}
<0.100.0> spawn_catcher: DANGEROUS MESSAGE: {<0.99.0>,test_spam}
<0.100.0> spawn_catcher: DANGEROUS MESSAGE: {<0.99.0>,test_spam}
<0.100.0> spawn_catcher: DANGEROUS MESSAGE: {<0.99.0>,test_spam}
<0.100.0> spawn_catcher: DANGEROUS MESSAGE: {<0.99.0>,test_spam}
<0.100.0> spawn_catcher: DANGEROUS MESSAGE: {<0.99.0>,test_spam}
<0.100.0> spawn_catcher: DANGEROUS MESSAGE: {<0.99.0>,test_spam}
<0.100.0> spawn_catcher: DANGEROUS MESSAGE: {<0.99.0>,test_spam}
<0.100.0> spawn_catcher: DANGEROUS MESSAGE: {<0.99.0>,test_spam}
<0.100.0> spawn_catcher: DANGEROUS MESSAGE: {<0.99.0>,test_spam}
<0.100.0> spawn_catcher: DANGEROUS MESSAGE: {<0.99.0>,test_spam}
<0.100.0> spawn_catcher: DANGEROUS MESSAGE: {<0.99.0>,test_spam}
<0.100.0> spawn_catcher: DANGEROUS MESSAGE: {<0.99.0>,test_spam}
<0.100.0> spawn_catcher: DANGEROUS MESSAGE: {<0.99.0>,test_spam}
<0.100.0> spawn_catcher: DANGEROUS MESSAGE: {<0.99.0>,test_spam}
<0.100.0> spawn_catcher: DANGEROUS MESSAGE: {<0.99.0>,test_spam}
<0.100.0> spawn_catcher: DANGEROUS MESSAGE: {<0.99.0>,test_spam}
<0.100.0> spawn_catcher: DANGEROUS MESSAGE: {<0.99.0>,test_spam}
<0.100.0> spawn_catcher: DANGEROUS MESSAGE: {<0.99.0>,test_spam}
<0.100.0> spawn_catcher: DANGEROUS MESSAGE: {<0.99.0>,test_spam}
<0.100.0> spawn_catcher: DANGEROUS MESSAGE: {<0.99.0>,test_spam}
<0.100.0> spawn_catcher: DANGEROUS MESSAGE: {<0.99.0>,test_spam}
<0.100.0> spawn_catcher: DANGEROUS MESSAGE: {<0.99.0>,test_spam}
<0.100.0> spawn_catcher: Getting ready
<0.100.0> spawn_catcher: Got first proper message. Sending reply.
THIS IS EXTREMELY UNLIKELY but it can happen.