A little confused about spawning new processes

I’m building a bot for a server just for fun and get a test of elixir more ‘in-depth’, the bot has to save state so I’m using Agents, everything is kinda working as intended for now. I’d like that the bot “creates” a new game room if it is in another server, so, it should spawn another process and save the new state to that pid, right? That’s what I understood about processes until now, how can I spawn a new process if a given command is executed? For example

>>start (Which is a command from the server to trigger the start_link() function)

Bot spawns a new process with an empty state but if I execute the >>start command again I get an error saying that the process is already running (I will save the id of the server so no more than one process is spawned for server), obviously not spawning a new process, so that’s my doubt, how can I spawn new processes with an empty state? Is that the way it should be done? Every process spawned has it’s own state and do not interfere with another one?

I’m using the module name as the name for the process, maybe that’s why the >>start command is always referenced to the same process? Sorry if I was not too clear with my issue, please tell me and I’ll elaborate.

Eyup, don’t name your process, hold the pid instead. :slight_smile:

And yes, every process’s state/stack is distinct. :slight_smile:

1 Like

Alright, that problem is solved then, after holding the pid in a variable I need to access it from other blocks in the same module, the commands are in a case, since the >>start command starts the link with the Agent I can’t access the variable outside of that block, which can be the most efficient way of getting that pid? Thanks for the help.

Well I certainly would not use an Agent, but to pass it out of a block you just return it from that block, what issue were you having with that?

Ah, sorry, not a block, it was a clause, as far as I know you can’t return values from case clauses, right?

I have something like this:

case msg.content do
  @prefix <> "start" ->
    # Main server
    {:ok, pid} = Cass.Server.start_link(
      %{name: msg.author.username, id: msg.author.id}
    )

  @prefix <> "queue" ->
    # here I need to get the pid to know it's state
end

Or what would you recommend me to do in this situation?

https://hexdocs.pm/elixir/Registry.html

Also, the result of the last expression in a case clause gets returned:

value =
  case true do
    true -> 1
    false -> 0
  end

value # 0
1 Like

*Everything* returns something, and your @prefix <> "start" -> is returning {:ok, pid}, just assign the output of the case msg.content do to a variable and pass it around in whatever you are holding state in. ^.^

This would work for a single node, but not multi-node, so it depends on your use case.

1 Like

Ah, I didn’t know that, thanks both for your input! :sweat_smile:

Argh, sorry but I think that I should explain my issue a little better and give more context, I’m using a library for Discord and the way that you listen for events is through a handle_event function and then you match the contents of the message with, well, what you’re expecting, this is part of my code:

def handle_event({:MESSAGE_CREATE, {msg}, _ws_state}, state) do
    case msg.content do
      @prefix <> "start" ->
        {:ok, pid} = Cass.Server.start_link(
          %{name: msg.author.username, id: msg.author.id}
        )
        pid # at first glance what I understood was this and that'd be it if I wanted to just return something out of this whole "case", right?
      @prefix <> "queue" ->
        Setup.get_agent(pid) # use the pid returned above as a parameter here?
    end
end

I’m getting errors saying that the pid variable doesn’t exist because of the scope, right? I mean, even if the process is running I can’t use that pid. I tried using the content of the variable as the return statement of the case, something like:

pid =
  case msg.content do
    @prefix <> "start" ->
      Cass.Server.start_link(
        %{name: msg.author.username, id: msg.author.id}
      )
    _ ->
      :ignore
  end

But, uh, I can’t use two case statements, so… I really don’t know how to initialize the process and pass the variable to get the state later on :frowning:

I need to explicitly initialize the process when the >>start command is specified because then how can I spawn the process? I tried moving the start_link function above the case but that’d match every time a message is sent and that’s a total mess, if I just wanted to use the bot on a single server it would be done already. I thought I had it but I’m still confused… well, not confused because I understand the process, I just don’t know how to implement it… :sweat:

Yeah you are not storing the pid anywhere here at all. ^.^

Assuming your state is a map, then you can do this:

def handle_event({:MESSAGE_CREATE, {msg}, _ws_state}, state) do
    case msg.content do
      @prefix <> "start" ->
        {:ok, pid} = Cass.Server.start_link(
          %{name: msg.author.username, id: msg.author.id}
        )
        %{state | pid: pid} # You return your updated state from this function, so you do that here
      @prefix <> "queue" ->
        Setup.get_agent(state.pid)
        state # Not changing the state so just passing it right back out
    end
end

So yeah, you were not actually storing the pid anywhere. ^.^

1 Like

For note, you can initialize your state map in your init callback by setting it to, oh, %{pid: nil} or something.

1 Like