Implemented Nickname cancellation using context, the good way
This commit is contained in:
parent
4aadbbc16c
commit
794342ab71
55
channel.go
55
channel.go
|
@ -1,6 +1,7 @@
|
||||||
package mock
|
package mock
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -40,7 +41,8 @@ type Channel struct {
|
||||||
// up to about 12 or so. check sameAuthorLimit.
|
// up to about 12 or so. check sameAuthorLimit.
|
||||||
incrAuthor uint8
|
incrAuthor uint8
|
||||||
|
|
||||||
busyWg sync.WaitGroup
|
// single-use write-once context, written on every JoinServer
|
||||||
|
ctx context.Context
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -61,15 +63,25 @@ func (ch *Channel) Name() text.Rich {
|
||||||
return text.Rich{Content: ch.name}
|
return text.Rich{Content: ch.name}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Nickname sets the labeler to the nickname. It simulates heavy IO. This
|
||||||
|
// function stops as cancel is called in JoinServer, as Nickname is specially
|
||||||
|
// for that.
|
||||||
func (ch *Channel) Nickname(labeler cchat.LabelContainer) error {
|
func (ch *Channel) Nickname(labeler cchat.LabelContainer) error {
|
||||||
// Simulate IO.
|
// Borrow the parent's context and stop fetching if the context expires.
|
||||||
simulateAustralianInternet()
|
ctx, cancel := context.WithCancel(ch.ctx)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
// Simulate IO with cancellation. Ignore the error if it's a simulated time
|
||||||
|
// out, else return.
|
||||||
|
if err := simulateAustralianInternetCtx(ctx); err != nil && err != ErrTimedOut {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
labeler.SetLabel(ch.username)
|
labeler.SetLabel(ch.username)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ch *Channel) JoinServer(container cchat.MessagesContainer) (func(), error) {
|
func (ch *Channel) JoinServer(container cchat.MessagesContainer) (stop func(), err error) {
|
||||||
// Is this a fresh channel? If yes, generate messages with some IO latency.
|
// Is this a fresh channel? If yes, generate messages with some IO latency.
|
||||||
if len(ch.messages) == 0 || ch.messageixs == nil {
|
if len(ch.messages) == 0 || ch.messageixs == nil {
|
||||||
// Simulate IO.
|
// Simulate IO.
|
||||||
|
@ -94,8 +106,8 @@ func (ch *Channel) JoinServer(container cchat.MessagesContainer) (func(), error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize channels for use.
|
// Initialize context for cancellation.
|
||||||
doneCh := make(chan struct{})
|
ch.ctx, stop = context.WithCancel(context.Background())
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
ticker := time.NewTicker(4 * time.Second)
|
ticker := time.NewTicker(4 * time.Second)
|
||||||
|
@ -126,13 +138,13 @@ func (ch *Channel) JoinServer(container cchat.MessagesContainer) (func(), error)
|
||||||
var old = ch.randomOldMsg()
|
var old = ch.randomOldMsg()
|
||||||
ch.deleteMessage(MessageHeader{old.id, time.Now()}, container)
|
ch.deleteMessage(MessageHeader{old.id, time.Now()}, container)
|
||||||
|
|
||||||
case <-doneCh:
|
case <-ch.ctx.Done():
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return func() { doneCh <- struct{}{} }, nil
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ch *Channel) RawMessageContent(id string) (string, error) {
|
func (ch *Channel) RawMessageContent(id string) (string, error) {
|
||||||
|
@ -272,8 +284,8 @@ func (ch *Channel) nextID() (id uint32) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ch *Channel) SendMessage(msg cchat.SendableMessage) error {
|
func (ch *Channel) SendMessage(msg cchat.SendableMessage) error {
|
||||||
if simulateAustralianInternet() {
|
if err := simulateAustralianInternet(); err != nil {
|
||||||
return errors.New("Failed to send message: Australian Internet unsupported.")
|
return errors.Wrap(err, "Failed to send message")
|
||||||
}
|
}
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
|
@ -360,12 +372,29 @@ func randClamp(min, max int) int {
|
||||||
return rand.Intn(max-min) + min
|
return rand.Intn(max-min) + min
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ErrTimedOut is returned when the simulated IO decides to fail.
|
||||||
|
var ErrTimedOut = errors.New("Australian Internet unsupported.")
|
||||||
|
|
||||||
// simulate network latency
|
// simulate network latency
|
||||||
func simulateAustralianInternet() (lost bool) {
|
func simulateAustralianInternet() error {
|
||||||
|
return simulateAustralianInternetCtx(context.Background())
|
||||||
|
}
|
||||||
|
|
||||||
|
func simulateAustralianInternetCtx(ctx context.Context) (err error) {
|
||||||
var ms = randClamp(internetMinLatency, internetMaxLatency)
|
var ms = randClamp(internetMinLatency, internetMaxLatency)
|
||||||
<-time.After(time.Duration(ms) * time.Millisecond)
|
|
||||||
|
select {
|
||||||
|
case <-time.After(time.Duration(ms) * time.Millisecond):
|
||||||
|
// noop
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
}
|
||||||
|
|
||||||
// because australia, drop packet 20% of the time if internetCanFail is
|
// because australia, drop packet 20% of the time if internetCanFail is
|
||||||
// true.
|
// true.
|
||||||
return internetCanFail && rand.Intn(100) < 20
|
if internetCanFail && rand.Intn(100) < 20 {
|
||||||
|
return ErrTimedOut
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
10
service.go
10
service.go
|
@ -3,13 +3,13 @@ package mock
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/diamondburned/cchat"
|
"github.com/diamondburned/cchat"
|
||||||
"github.com/diamondburned/cchat/services"
|
"github.com/diamondburned/cchat/services"
|
||||||
"github.com/diamondburned/cchat/text"
|
"github.com/diamondburned/cchat/text"
|
||||||
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -32,8 +32,8 @@ func (s Service) Name() text.Rich {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s Service) RestoreSession(storage map[string]string) (cchat.Session, error) {
|
func (s Service) RestoreSession(storage map[string]string) (cchat.Session, error) {
|
||||||
if simulateAustralianInternet() {
|
if err := simulateAustralianInternet(); err != nil {
|
||||||
return nil, errors.New("Restore failed: server machine broke")
|
return nil, errors.Wrap(err, "Restore failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
username, ok := storage["username"]
|
username, ok := storage["username"]
|
||||||
|
@ -70,8 +70,8 @@ func (Authenticator) AuthenticateForm() []cchat.AuthenticateEntry {
|
||||||
|
|
||||||
func (Authenticator) Authenticate(form []string) (cchat.Session, error) {
|
func (Authenticator) Authenticate(form []string) (cchat.Session, error) {
|
||||||
// SLOW IO TIME.
|
// SLOW IO TIME.
|
||||||
if simulateAustralianInternet() {
|
if err := simulateAustralianInternet(); err != nil {
|
||||||
return nil, errors.New("Authentication timed out.")
|
return nil, errors.Wrap(err, "Authentication failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
return newSession(form[0]), nil
|
return newSession(form[0]), nil
|
||||||
|
|
Loading…
Reference in New Issue