Hi guys I am trying to make a party system using websockets where people can enter a queue and then get matched with 5 people similar to them. Right now I'm having trouble with this part:
type PartyHub struct {
Partys map[string]*Party
PartialPartys []*PartialParty
Queue []*Member
AddParty chan *Party
RemoveParty chan *Party
AddPartialParty chan *PartialParty
RemovePartialParty chan *PartialParty
EnterQueue chan *Member
LeaveQueue chan *Member
Mu sync.Mutex
}
// Run will begin monitoring the channels
// to register and unregister partys as they are
// created or destroyed
func (p *PartyHub) Run() {
for {
select {
case member := <-p.EnterQueue:
go p.SortMemberIntoParty(member)
go p.SortMemberIntoParty(member)
go p.SortMemberIntoParty(member)
go p.SortMemberIntoParty(member)
go p.SortMemberIntoParty(member)
log.Println(p.PartialPartys)
case party := <-p.AddPartialParty:
p.Mu.Lock()
defer p.Mu.Unlock()
p.PartialPartys = append(p.PartialPartys, party)
}
}
}
// SortMemberIntoParty will take a new user entering the queue and find an appropriate Party
// for the member to join, taking into account RankTollerance, Rank
func (p *PartyHub) SortMemberIntoParty(member *Member) {
p.Mu.Lock()
defer p.Mu.Unlock()
if len(p.PartialPartys) == 0 {
log.Println("Here")
newParty := &PartialParty{Accepting: true, Members: []*Member{member}}
p.AddPartialParty <- newParty
return
}
foundPartyForMember := false
for _, party := range p.PartialPartys {
goodFitForParty := true
for _, partyMember := range party.Members {
log.Println(member.Type == partyMember.Type, member.Rank >= partyMember.Rank-partyMember.RankTol, member.Rank <= partyMember.Rank+partyMember.RankTol)
if member.Type == partyMember.Type && member.Rank >= partyMember.Rank-partyMember.RankTol && member.Rank <= partyMember.Rank+partyMember.RankTol {
goodFitForParty = true
continue
} else {
goodFitForParty = false
break
}
}
if !goodFitForParty {
continue
} else {
foundPartyForMember = true
party.Mu.Lock()
defer party.Mu.Unlock()
party.Members = append(party.Members, member)
if len(party.Members) == 5 {
party.Accepting = false
go party.SendReadyCheck()
}
break
}
}
if !foundPartyForMember {
newParty := &PartialParty{Accepting: true, Members: []*Member{member}}
p.AddPartialParty <- newParty
}
log.Println("Sorting Members")
}
The only problem is, the 5 goroutines
seem to finish quicker than the data knows what happened.
For example: p.PartialPartys
says it has no parties.
What I need is to have p.PartialPartys
always up to date for every goroutine
that accesses that field of the PartyHub
struct I though the sync.Mutex
would do this for me but it doesn't seem to be the case, can anybody tell me the best way to keep all my goroutines in sync with the same data?
So with this implementation, none of your five goroutines are going to be be able to run in parallel because they are all trying to acquite the p.Mu
mutex. And looking at the way you're using the p.AddPartialParty
channel, I wouldn't be surprised if the code can deadlock.
Consider the following sequence of events:
SortMemberIntoParty
goroutines starts running and acquires the mutex.p.AddPartialParty
, which is received by Run
. Run
then tries to acquire the mutex, so blocks.SortMemberIntoParty
goroutine completes and releases the mutex.SortMemberIntoParty
goroutine acquires the mutex, and tries to send another value to p.AddPartialParty
.Run
is still waiting on the mutex before it gets back to the select
statement).So now you've got a blocked goroutine holding a lock needed by the receiving end of the channel. Also note that at (4) you won't see the new PartialParty
because Run
hasn't managed to add it yet.
If you do need the mutex, then it might be easier to just have your SortMemberIntoParty
goroutine update p.PartialPartys
directly rather than using the channel: you already know that no one else will be accessing the variable concurrently.
It's also worth remembering that this mutex essentially means that all SortMemberIntoParty
goroutines will be serialised. If you were using goroutines in the hope of achieving parallelism here, the mutex defeats that.