I know it's pretty much impossible to have a GenServer process call itself because you essentially hit a deadlock. But, I'm curious if there's a preferred way to do this kind of thing.
Assume the following scenario: I've got a queue that I'm popping things from. If the queue is ever empty, I want to refill it. I could structure it like so:
def handle_call(:refill_queue, state) do
  new_state = put_some_stuff_in_queue(state)
  {:reply, new_state}
end
def handle_call(:pop, state) do
  if is_empty_queue(state) do
    GenServer.call(self, :refill_queue)
  end
  val,new_state = pop_something(state)
  {:reply, val, new_state}
end
The big problem here is that this will deadlock when we try to refill the queue. One solution that I've used in the past is to use cast more so it doesn't deadlock. Like so (change call to cast for refill)
def handle_cast(:refill_queue, state) do
But in this case, I think it won't work, since the async cast to refill the queue might return in the pop case before actually filling the queue meaning I'll try to pop off an empty queue.
Anyways, the core question is: What is the best way to handle this? I assume the answer is to just call put_some_stuff_in_queue directly inside the pop call, but I wanted to check. In other words, it seems like the right thing to do is make handle_call and handle_cast as simple as possible and basically just wrappers to other functions where the real work happens. Then, create as many handle_* functions as you need to cover all the possible cases you'll deal with, rather than having handle_call(:foo) in turn call handle_call(:bar).