mirror of
https://github.com/ergochat/ergo.git
synced 2024-11-10 22:19:31 +01:00
Squash a bunch of possible races
This commit is contained in:
parent
1977d03faf
commit
c911ff2bcd
@ -13,6 +13,7 @@ import (
|
|||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/DanielOaks/girc-go/ircmsg"
|
"github.com/DanielOaks/girc-go/ircmsg"
|
||||||
@ -43,23 +44,24 @@ type Client struct {
|
|||||||
channels ChannelSet
|
channels ChannelSet
|
||||||
class *OperClass
|
class *OperClass
|
||||||
ctime time.Time
|
ctime time.Time
|
||||||
|
destroyMutex sync.Mutex
|
||||||
flags map[Mode]bool
|
flags map[Mode]bool
|
||||||
isDestroyed bool
|
|
||||||
isQuitting bool
|
|
||||||
hasQuit bool
|
hasQuit bool
|
||||||
hops int
|
hops int
|
||||||
hostname string
|
hostname string
|
||||||
rawHostname string
|
|
||||||
vhost string
|
|
||||||
idleTimer *time.Timer
|
idleTimer *time.Timer
|
||||||
|
isDestroyed bool
|
||||||
|
isQuitting bool
|
||||||
monitoring map[string]bool
|
monitoring map[string]bool
|
||||||
nick string
|
nick string
|
||||||
nickCasefolded string
|
nickCasefolded string
|
||||||
nickMaskString string // cache for nickmask string since it's used with lots of replies
|
|
||||||
nickMaskCasefolded string
|
nickMaskCasefolded string
|
||||||
|
nickMaskString string // cache for nickmask string since it's used with lots of replies
|
||||||
operName string
|
operName string
|
||||||
quitTimer *time.Timer
|
|
||||||
quitMessageSent bool
|
quitMessageSent bool
|
||||||
|
quitMutex sync.Mutex
|
||||||
|
quitTimer *time.Timer
|
||||||
|
rawHostname string
|
||||||
realname string
|
realname string
|
||||||
registered bool
|
registered bool
|
||||||
saslInProgress bool
|
saslInProgress bool
|
||||||
@ -67,7 +69,9 @@ type Client struct {
|
|||||||
saslValue string
|
saslValue string
|
||||||
server *Server
|
server *Server
|
||||||
socket *Socket
|
socket *Socket
|
||||||
|
timerMutex sync.Mutex
|
||||||
username string
|
username string
|
||||||
|
vhost string
|
||||||
whoisLine string
|
whoisLine string
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -229,6 +233,9 @@ func (client *Client) Active() {
|
|||||||
|
|
||||||
// Touch marks the client as alive.
|
// Touch marks the client as alive.
|
||||||
func (client *Client) Touch() {
|
func (client *Client) Touch() {
|
||||||
|
client.timerMutex.Lock()
|
||||||
|
defer client.timerMutex.Unlock()
|
||||||
|
|
||||||
if client.quitTimer != nil {
|
if client.quitTimer != nil {
|
||||||
client.quitTimer.Stop()
|
client.quitTimer.Stop()
|
||||||
}
|
}
|
||||||
@ -242,6 +249,9 @@ func (client *Client) Touch() {
|
|||||||
|
|
||||||
// Idle resets the timeout handlers and sends the client a PING.
|
// Idle resets the timeout handlers and sends the client a PING.
|
||||||
func (client *Client) Idle() {
|
func (client *Client) Idle() {
|
||||||
|
client.timerMutex.Lock()
|
||||||
|
defer client.timerMutex.Unlock()
|
||||||
|
|
||||||
client.Send(nil, "", "PING", client.nick)
|
client.Send(nil, "", "PING", client.nick)
|
||||||
|
|
||||||
if client.quitTimer == nil {
|
if client.quitTimer == nil {
|
||||||
@ -435,6 +445,8 @@ func (client *Client) ChangeNickname(nickname string) error {
|
|||||||
|
|
||||||
// Quit sends the given quit message to the client (but does not destroy them).
|
// Quit sends the given quit message to the client (but does not destroy them).
|
||||||
func (client *Client) Quit(message string) {
|
func (client *Client) Quit(message string) {
|
||||||
|
client.quitMutex.Lock()
|
||||||
|
defer client.quitMutex.Unlock()
|
||||||
if !client.quitMessageSent {
|
if !client.quitMessageSent {
|
||||||
quitMsg := ircmsg.MakeMessage(nil, client.nickMaskString, "QUIT", message)
|
quitMsg := ircmsg.MakeMessage(nil, client.nickMaskString, "QUIT", message)
|
||||||
quitLine, _ := quitMsg.Line()
|
quitLine, _ := quitMsg.Line()
|
||||||
@ -442,13 +454,15 @@ func (client *Client) Quit(message string) {
|
|||||||
errorMsg := ircmsg.MakeMessage(nil, "", "ERROR", message)
|
errorMsg := ircmsg.MakeMessage(nil, "", "ERROR", message)
|
||||||
errorLine, _ := errorMsg.Line()
|
errorLine, _ := errorMsg.Line()
|
||||||
|
|
||||||
client.socket.FinalData = quitLine + errorLine
|
client.socket.SetFinalData(quitLine + errorLine)
|
||||||
client.quitMessageSent = true
|
client.quitMessageSent = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// destroy gets rid of a client, removes them from server lists etc.
|
// destroy gets rid of a client, removes them from server lists etc.
|
||||||
func (client *Client) destroy() {
|
func (client *Client) destroy() {
|
||||||
|
client.destroyMutex.Lock()
|
||||||
|
defer client.destroyMutex.Unlock()
|
||||||
if client.isDestroyed {
|
if client.isDestroyed {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,6 @@ import (
|
|||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
@ -26,12 +25,16 @@ var (
|
|||||||
|
|
||||||
// Socket represents an IRC socket.
|
// Socket represents an IRC socket.
|
||||||
type Socket struct {
|
type Socket struct {
|
||||||
Closed bool
|
|
||||||
conn net.Conn
|
conn net.Conn
|
||||||
reader *bufio.Reader
|
reader *bufio.Reader
|
||||||
|
|
||||||
MaxSendQBytes uint64
|
MaxSendQBytes uint64
|
||||||
FinalData string // what to send when we die
|
|
||||||
|
closed bool
|
||||||
|
closedMutex sync.Mutex
|
||||||
|
|
||||||
|
finalData string // what to send when we die
|
||||||
|
finalDataMutex sync.Mutex
|
||||||
|
|
||||||
lineToSendExists chan bool
|
lineToSendExists chan bool
|
||||||
linesToSend []string
|
linesToSend []string
|
||||||
@ -50,10 +53,12 @@ func NewSocket(conn net.Conn, maxSendQBytes uint64) Socket {
|
|||||||
|
|
||||||
// Close stops a Socket from being able to send/receive any more data.
|
// Close stops a Socket from being able to send/receive any more data.
|
||||||
func (socket *Socket) Close() {
|
func (socket *Socket) Close() {
|
||||||
if socket.Closed {
|
socket.closedMutex.Lock()
|
||||||
|
defer socket.closedMutex.Unlock()
|
||||||
|
if socket.closed {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
socket.Closed = true
|
socket.closed = true
|
||||||
|
|
||||||
// force close loop to happen if it hasn't already
|
// force close loop to happen if it hasn't already
|
||||||
go socket.timedFillLineToSendExists(200 * time.Millisecond)
|
go socket.timedFillLineToSendExists(200 * time.Millisecond)
|
||||||
@ -88,7 +93,7 @@ func (socket *Socket) CertFP() (string, error) {
|
|||||||
|
|
||||||
// Read returns a single IRC line from a Socket.
|
// Read returns a single IRC line from a Socket.
|
||||||
func (socket *Socket) Read() (string, error) {
|
func (socket *Socket) Read() (string, error) {
|
||||||
if socket.Closed {
|
if socket.IsClosed() {
|
||||||
return "", io.EOF
|
return "", io.EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,7 +118,7 @@ func (socket *Socket) Read() (string, error) {
|
|||||||
|
|
||||||
// Write sends the given string out of Socket.
|
// Write sends the given string out of Socket.
|
||||||
func (socket *Socket) Write(data string) error {
|
func (socket *Socket) Write(data string) error {
|
||||||
if socket.Closed {
|
if socket.IsClosed() {
|
||||||
return io.EOF
|
return io.EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,9 +126,7 @@ func (socket *Socket) Write(data string) error {
|
|||||||
socket.linesToSend = append(socket.linesToSend, data)
|
socket.linesToSend = append(socket.linesToSend, data)
|
||||||
socket.linesToSendMutex.Unlock()
|
socket.linesToSendMutex.Unlock()
|
||||||
|
|
||||||
if !socket.Closed {
|
go socket.timedFillLineToSendExists(15 * time.Second)
|
||||||
go socket.timedFillLineToSendExists(15 * time.Second)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -138,9 +141,22 @@ func (socket *Socket) timedFillLineToSendExists(duration time.Duration) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetFinalData sets the final data to send when the SocketWriter closes.
|
||||||
|
func (socket *Socket) SetFinalData(data string) {
|
||||||
|
socket.finalDataMutex.Lock()
|
||||||
|
socket.finalData = data
|
||||||
|
socket.finalDataMutex.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsClosed returns whether the socket is closed.
|
||||||
|
func (socket *Socket) IsClosed() bool {
|
||||||
|
socket.closedMutex.Lock()
|
||||||
|
defer socket.closedMutex.Unlock()
|
||||||
|
return socket.closed
|
||||||
|
}
|
||||||
|
|
||||||
// RunSocketWriter starts writing messages to the outgoing socket.
|
// RunSocketWriter starts writing messages to the outgoing socket.
|
||||||
func (socket *Socket) RunSocketWriter() {
|
func (socket *Socket) RunSocketWriter() {
|
||||||
var errOut bool
|
|
||||||
for {
|
for {
|
||||||
// wait for new lines
|
// wait for new lines
|
||||||
select {
|
select {
|
||||||
@ -148,7 +164,7 @@ func (socket *Socket) RunSocketWriter() {
|
|||||||
socket.linesToSendMutex.Lock()
|
socket.linesToSendMutex.Lock()
|
||||||
|
|
||||||
// check if we're closed
|
// check if we're closed
|
||||||
if socket.Closed {
|
if socket.IsClosed() {
|
||||||
socket.linesToSendMutex.Unlock()
|
socket.linesToSendMutex.Unlock()
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -169,7 +185,7 @@ func (socket *Socket) RunSocketWriter() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if socket.MaxSendQBytes < sendQBytes {
|
if socket.MaxSendQBytes < sendQBytes {
|
||||||
socket.FinalData = "\r\nERROR :SendQ Exceeded\r\n"
|
socket.SetFinalData("\r\nERROR :SendQ Exceeded\r\n")
|
||||||
socket.linesToSendMutex.Unlock()
|
socket.linesToSendMutex.Unlock()
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -184,24 +200,30 @@ func (socket *Socket) RunSocketWriter() {
|
|||||||
if 0 < len(data) {
|
if 0 < len(data) {
|
||||||
_, err := socket.conn.Write([]byte(data))
|
_, err := socket.conn.Write([]byte(data))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errOut = true
|
|
||||||
fmt.Println(err.Error())
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if errOut || socket.Closed {
|
if socket.IsClosed() {
|
||||||
// error out or we've been closed
|
// error out or we've been closed
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !socket.Closed {
|
// force closure of socket
|
||||||
socket.Closed = true
|
socket.closedMutex.Lock()
|
||||||
|
if !socket.closed {
|
||||||
|
socket.closed = true
|
||||||
}
|
}
|
||||||
|
socket.closedMutex.Unlock()
|
||||||
|
|
||||||
// write error lines
|
// write error lines
|
||||||
if 0 < len(socket.FinalData) {
|
socket.finalDataMutex.Lock()
|
||||||
socket.conn.Write([]byte(socket.FinalData))
|
if 0 < len(socket.finalData) {
|
||||||
|
socket.conn.Write([]byte(socket.finalData))
|
||||||
}
|
}
|
||||||
|
socket.finalDataMutex.Unlock()
|
||||||
|
|
||||||
|
// close the connection
|
||||||
socket.conn.Close()
|
socket.conn.Close()
|
||||||
|
|
||||||
// empty the lineToSendExists channel
|
// empty the lineToSendExists channel
|
||||||
|
@ -4,10 +4,16 @@
|
|||||||
|
|
||||||
package irc
|
package irc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
type WhoWasList struct {
|
type WhoWasList struct {
|
||||||
buffer []*WhoWas
|
buffer []*WhoWas
|
||||||
start int
|
start int
|
||||||
end int
|
end int
|
||||||
|
|
||||||
|
accessMutex sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
type WhoWas struct {
|
type WhoWas struct {
|
||||||
@ -25,6 +31,9 @@ func NewWhoWasList(size uint) *WhoWasList {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (list *WhoWasList) Append(client *Client) {
|
func (list *WhoWasList) Append(client *Client) {
|
||||||
|
list.accessMutex.Lock()
|
||||||
|
defer list.accessMutex.Unlock()
|
||||||
|
|
||||||
list.buffer[list.end] = &WhoWas{
|
list.buffer[list.end] = &WhoWas{
|
||||||
nicknameCasefolded: client.nickCasefolded,
|
nicknameCasefolded: client.nickCasefolded,
|
||||||
nickname: client.nick,
|
nickname: client.nick,
|
||||||
@ -39,6 +48,9 @@ func (list *WhoWasList) Append(client *Client) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (list *WhoWasList) Find(nickname string, limit int64) []*WhoWas {
|
func (list *WhoWasList) Find(nickname string, limit int64) []*WhoWas {
|
||||||
|
list.accessMutex.RLock()
|
||||||
|
defer list.accessMutex.RUnlock()
|
||||||
|
|
||||||
results := make([]*WhoWas, 0)
|
results := make([]*WhoWas, 0)
|
||||||
|
|
||||||
casefoldedNickname, err := CasefoldName(nickname)
|
casefoldedNickname, err := CasefoldName(nickname)
|
||||||
@ -59,6 +71,9 @@ func (list *WhoWasList) Find(nickname string, limit int64) []*WhoWas {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (list *WhoWasList) prev(index int) int {
|
func (list *WhoWasList) prev(index int) int {
|
||||||
|
list.accessMutex.RLock()
|
||||||
|
defer list.accessMutex.RUnlock()
|
||||||
|
|
||||||
index--
|
index--
|
||||||
if index < 0 {
|
if index < 0 {
|
||||||
index += len(list.buffer)
|
index += len(list.buffer)
|
||||||
|
Loading…
Reference in New Issue
Block a user