#Reading from a channel inside goroutine blocking the thread

49 messages · Page 1 of 1 (latest)

exotic sluice
#

Hey there,
so I have a map channel to store connections

type TCP struct {
    listener net.Listener
    sockets  map[int]chan Connection
}
    for {
        conn, err := t.listener.Accept()

        if err != nil {
            slog.Error("server error: ", "err", err)
            os.Exit(2)
        }

        newConn := NewConnection(conn, id)

        slog.Info("new conn", "val", newConn)

        id++

        t.sockets[id] = make(chan Connection, 1)
        t.sockets[id] <- newConn

        slog.Info("total sockets", "len", len(t.sockets))

        go handleConnection(&newConn, t.sockets)
    }
``` now deep inside `handleConnection`  I have 
```go
func newUserJoined(room *shared.MultiplayerRoom, sockets map[int]chan Connection) {
    // Finding the user from connections for now due to import cycle.
    slog.Info("sockets", "len", sockets)
    for _, user := range room.Users {
        slog.Info("starting the process to send join notification")

        conn := <-sockets[user.Id]

        slog.Info("sending new user join to user", "id", user.Id)

        slog.Info("conn info", "val", conn)

        conn.Write(&shared.TCPCommand[shared.MultiplayerRoom]{
            Command: shared.NEW_USER_JOINED,
            Data:    *room,
        })

    }
}``` where reading `        conn := <-sockets[user.Id]` just blocks the thread
#

slog.Info("sockets", "len", sockets)
logs 2024/06/14 13:10:51 INFO sockets len="map[1:0x14000116180 2:0x140001162a0]"

median belfry
#

Reading from a channel empties it, so when a new user joins, you're storing their connection in a channel. I think the issue is that you're reading from it when a new user joins and then if another user joins the channel is empty so it will block.
I might be missing something but you probably don't want a channel and just want a map with a mutex

exotic sluice
#

Reading from a channel empties it
Yes but this is the only place i am reading it right?

median belfry
#

You're reading it on every iteration of the loop

exotic sluice
#

But can I share a map across like that through goroutines?

exotic sluice
#

newConn := NewConnection(conn, id)

    slog.Info("new conn", "val", newConn)

    id++

    t.sockets[id] = make(chan Connection, 1)
    t.sockets[id] <- newConn
#
 newConn := NewConnection(conn, id)

        slog.Info("new conn", "val", newConn)

        id++

        t.sockets[id] = make(chan Connection, 1)
        t.sockets[id] <- newConn```
median belfry
#

Sorry, I assumed that newUserJoined would be called every time a user joins.

exotic sluice
#
    // Send a message on the client so a re-render can happen
    newUserJoined(&Rooms[idx], sockets)

func newUserJoined(room *shared.MultiplayerRoom, sockets map[int]chan Connection) {
    // Finding the user from connections for now due to import cycle.
    slog.Info("sockets", "len", sockets)
    for _, user := range room.Users {
        slog.Info("starting the process to send join notification")

        conn := <-sockets[user.Id]

        slog.Info("sending new user join to user", "id", user.Id)

        slog.Info("conn info", "val", conn)

        conn.Write(&shared.TCPCommand[shared.MultiplayerRoom]{
            Command: shared.NEW_USER_JOINED,
            Data:    *room,
        })

    }
}```
median belfry
#

User 1 joins, their connection is stored
User 2 joins, their connection is stored, you read user1 from the channel
User 3 joins, their connection is stored, you try to read user1 from the channel but it's empty

Something like that?

exotic sluice
#

so basically I try to read all the user in the channels whenever a new user joins rn I have only 2 users

#

so no third case for rn, which would come later. And I guess you're right that map and mutex should be fine

#

but can I just pass a map around like that through different goroutines

#

go handleConnection(&newConn, t.sockets)

#
        newConn := NewConnection(conn, id)

        id++

        t.sockets[id] = newConn

        slog.Info("total sockets", "len", len(t.sockets))

        go handleConnection(&newConn, t.sockets)
``` for this type def 
```go
    sockets  map[int]Connection
``` conn goes nil in the goroutine for some reason
#

im just trying to read data from the goroutine and never write to it..

median belfry
#

A read from the channel should only block if it's empty.

Is the rest of the code on github or anything like that?

exotic sluice
#

yea.

#

ig channel would not be for me since I don't need to empty to the channel

#

2024/06/14 13:35:55 INFO conn info val="{conn:<nil> Id:0 previous:[]}"

type TCP struct {
    listener net.Listener
    sockets  map[int]Connection
}``` for this type
#

the socket value is that,conn returns nil inside the goroutine

median belfry
#

Is that for user 1 when the second user joins or what is the scenario?

exotic sluice
#

yea, so basically just for everyone

#

since I have like user ids, and that ids correspond to the map.

#

so I can't just use them to get the connection of the user, which is different for all users, and then send them a notification

median belfry
#

It only logs that line once?

exotic sluice
#

Yea as it blocks the channel, but rn im not trying to do channel thing since I dont want it to be empty

median belfry
#

This {conn:<nil> Id:0 previous:[]} suggests that the Connection is empty. I'm struggling to mentally follow the flow, potentially a skill issue on my part, but you could try adding the mutex lock. It's potentially that it's being read from before it's been written to

exotic sluice
#

so instead i want someway so i can access my map inside the goroutine

exotic sluice
#

The flow basically is

#

Everytime a connection is established it is added to sockets map, then after when a connection joins and sends a command from the client “JOIN_ROOM” then we need to notify all users in a particular room about that

median belfry
#

Can you show me all the log output from when the server starts?

exotic sluice
# median belfry Can you show me all the log output from when the server starts?
2024/06/14 13:35:37 INFO Starting server on  port=5555
2024/06/14 13:35:37 INFO Accepting connections addr=[::]:5555
2024/06/14 13:35:43 INFO new conn val="{conn:0x14000068048 Id:0 previous:[]}"
2024/06/14 13:35:43 INFO total sockets len=1
2024/06/14 13:35:43 INFO rcvd message msg="\x00\n"
2024/06/14 13:35:43 INFO num of bytes n=2
2024/06/14 13:35:43 INFO rcvd message msg="\x01\n"
2024/06/14 13:35:43 INFO created a room with id=534
2024/06/14 13:35:43 INFO num of bytes n=2
2024/06/14 13:35:55 INFO new conn val="{conn:0x14000068118 Id:1 previous:[]}"
2024/06/14 13:35:55 INFO total sockets len=2
2024/06/14 13:35:55 INFO rcvd message msg="\x00\n"
2024/06/14 13:35:55 INFO num of bytes n=2
2024/06/14 13:35:55 INFO rcvd message msg="\x02534\n"
2024/06/14 13:35:55 INFO Total users in room val="[{Id:0} {Id:1}]"
2024/06/14 13:35:55 INFO sockets len="map[1:{conn:0x14000068048 Id:0 previous:[]} 2:{conn:0x14000068118 Id:1 previous:[]}]"
2024/06/14 13:35:55 INFO starting the process to send join notification
2024/06/14 13:35:55 INFO sending new user join to user id=0
2024/06/14 13:35:55 INFO conn info val="{conn:<nil> Id:0 previous:[]}"
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x2 addr=0x50 pc=0x102c9f25c]
#
func newUserJoined(room *shared.MultiplayerRoom, sockets map[int]Connection) {
    // Finding the user from connections for now due to import cycle.
    slog.Info("sockets", "len", sockets)
``` this is from here
#

as soon as it gewts into the loop it goes nil very strange

#
    for _, user := range room.Users {
        slog.Info("starting the process to send join notification")

        conn := sockets[user.Id]

        slog.Info("sending new user join to user", "id", user.Id)

        slog.Info("conn info", "val", conn)

        conn.Write(&shared.TCPCommand[shared.MultiplayerRoom]{
            Command: shared.NEW_USER_JOINED,
            Data:    *room,
        })

    }
``` nil here
median belfry
#

len="map[1:{conn:0x14000068048 Id:0 previous:[]} 2:{conn:0x14000068118 Id:1 previous:[]}]"
The key is 1 but the Id is 0 in the conn, is that right?

exotic sluice
#

oh fuck

#

just noticed that now shit

median belfry
#

That's potentially why your channel was blocking too. I'd still go for a map though since you don't need to consume the Connection

#
conn, ok := sockets[user.Id]
if !ok {
  // handle missing connection
}

It might be a good idea to use something like the above when you're accessing the map as that would have caught it