Channels work now I think

This commit is contained in:
Neale Pickett 2020-04-12 20:40:52 -06:00
parent d5f6038670
commit cdf3869454
7 changed files with 179 additions and 69 deletions

92
book.go Normal file
View File

@ -0,0 +1,92 @@
package main
import (
"io"
"log"
)
type Book struct {
entries map[string]*Repeater
events chan bookEvent
}
func NewBook() Book {
return Book{
entries: make(map[string]*Repeater),
events: make(chan bookEvent, 5),
}
}
type bookEventType int
const (
joinEvent = bookEventType(iota)
partEvent
sendEvent
)
type bookEvent struct {
eventType bookEventType
name string
w io.Writer
p []byte
}
func (b Book) Join(name string, w io.Writer) {
b.events <- bookEvent{
eventType: joinEvent,
name: name,
w: w,
}
}
func (b Book) Part(name string, w io.Writer) {
b.events <- bookEvent{
eventType: partEvent,
name: name,
w: w,
}
}
func (b Book) Send(name string, p []byte) {
b.events <- bookEvent{
eventType: sendEvent,
name: name,
p: p,
}
}
func (b Book) Run() {
for {
b.loop()
}
}
func (b Book) loop() {
event := <-b.events
repeater, ok := b.entries[event.name]
switch event.eventType {
case joinEvent:
if ! ok {
repeater = NewRepeater()
b.entries[event.name] = repeater
}
repeater.Join(event.w)
case partEvent:
if ! ok {
log.Println("WARN: Parting an empty channel:", event.name)
break
}
repeater.Part(event.w)
if len(repeater.subscribers) == 0 {
delete(b.entries, event.name)
}
case sendEvent:
if ! ok {
log.Println("WARN: Sending to an empty channel:", event.name)
break
}
repeater.Send(event.p)
}
}

59
book_test.go Normal file
View File

@ -0,0 +1,59 @@
package main
import (
"bytes"
"testing"
)
func TestBook(t *testing.T) {
b := NewBook()
buf1 := bytes.NewBufferString("buf1")
b.Join("moo", buf1)
b.loop()
if len(b.entries) != 1 {
t.Error("Wrong number of entries")
}
// Send to an empty channel
b.Send("merf", []byte("goober"))
b.loop()
if buf1.String() != "buf1" {
t.Error("Sending to empty channel sent to non-empty channel")
}
// Send to a non-empty channel!
b.Send("moo", []byte("goober"))
b.loop()
if buf1.String() != "buf1goober" {
t.Error("Sending didn't work")
}
// Join another client
buf2 := bytes.NewBufferString("buf2")
b.Join("moo", buf2)
b.loop()
// Send to both
b.Send("moo", []byte("snerk"))
b.loop()
if buf1.String() != "buf1goobersnerk" {
t.Error("Send to 2-member channel busted", buf1)
}
if buf2.String() != "buf2snerk" {
t.Error("Send to 2-member channel busted", buf2)
}
// Part a client
b.Part("moo", buf1)
b.loop()
b.Send("moo", []byte("peanut"))
b.loop()
if buf1.String() != "buf1goobersnerk" {
t.Error("Parted channel but still getting messages", buf1)
}
if buf2.String() != "buf2snerkpeanut" {
t.Error("Someone else parting somehow messed up sends", buf2)
}
}

30
main.go
View File

@ -6,6 +6,22 @@ import (
"golang.org/x/net/websocket"
)
func (ch Channel) Write(msg Message) {
for i, o := range clients {
if o.active == false {
nclients := len(clients)
clients[i] = clients[nclients - 1]
clients[nclients - 1] = Client{}
clients = clients[:nclients - 1]
} else if o == c {
// Don't send it back to the sending client
} else {
o.ws.Write(msg)
}
}
}
type Client struct {
ws *websocket.Conn
active bool
@ -14,6 +30,7 @@ type Client struct {
var clients []Client
func (c Client) Chat() {
websocket.Message.Receive
for c.active {
buf := make([]byte, 800)
n, err := c.ws.Read(buf)
@ -21,19 +38,6 @@ func (c Client) Chat() {
c.active = false
}
buf = buf[:n]
for i, o := range clients {
if o.active == false {
nclients := len(clients)
clients[i] = clients[nclients - 1]
clients[nclients - 1] = Client{}
clients = clients[:nclients - 1]
} else if o == c {
// Don't send it back to the sending client
} else {
o.ws.Write(buf)
}
}
}
}

View File

@ -5,62 +5,31 @@ import (
)
type Repeater struct {
joins chan io.Writer
parts chan io.Writer
sends chan []byte
subscribers []io.Writer
}
func NewRepeater() *Repeater {
return &Repeater{
joins: make(chan io.Writer, 5),
parts: make(chan io.Writer, 5),
sends: make(chan []byte, 5),
subscribers: make([]io.Writer, 0, 20),
}
}
func (r *Repeater) Join(w io.Writer) {
r.joins <- w
r.subscribers = append(r.subscribers, w)
}
func (r *Repeater) Part(w io.Writer) {
r.parts <- w
for i, s := range r.subscribers {
if s == w {
nsubs := len(r.subscribers)
r.subscribers[i] = r.subscribers[nsubs-1]
r.subscribers = r.subscribers[:nsubs-1]
}
}
}
func (r *Repeater) Send(p []byte) {
r.sends <- p
}
func (r *Repeater) Close() {
close(r.sends)
}
func (r *Repeater) Run() {
for r.loop() {}
}
func (r *Repeater) loop() bool {
select {
case w := <- r.joins:
// Add subscriber
r.subscribers = append(r.subscribers, w)
case w := <- r.parts:
// Remove subscriber
for i, s := range r.subscribers {
if s == w {
nsubs := len(r.subscribers)
r.subscribers[i] = r.subscribers[nsubs-1]
r.subscribers = r.subscribers[:nsubs-1]
}
}
case p, ok := <- r.sends:
if ! ok {
return false
}
for _, s := range r.subscribers {
s.Write(p)
}
for _, s := range r.subscribers {
s.Write(p)
}
return true
}

View File

@ -10,21 +10,17 @@ func TestRepeater(t *testing.T) {
buf1 := bytes.NewBufferString("buf1")
r.Join(buf1)
r.loop()
if len(r.subscribers) != 1 {
t.Error("Joining did nothing")
}
r.Send([]byte("moo"))
r.loop()
if buf1.String() != "buf1moo" {
t.Error("Client 1 not repeating", buf1)
}
buf2 := bytes.NewBufferString("buf2")
r.Join(buf2)
r.loop()
r.Send([]byte("bar"))
r.loop()
if buf1.String() != "buf1moobar" {
t.Error("Client 1 not repeating", buf1)
}
@ -33,21 +29,11 @@ func TestRepeater(t *testing.T) {
}
r.Part(buf1)
r.loop()
r.Send([]byte("baz"))
r.loop()
if buf1.String() != "buf1moobar" {
t.Error("Client 1 still getting data after part", buf1)
}
if buf2.String() != "buf2barbaz" {
t.Error("Client 2 not getting data after part", buf2)
}
r.Close()
if r.loop() {
t.Error("Closed send didn't terminate loop")
}
if r.loop() {
t.Error("Second loop in terminated channel didn't terminate")
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

View File

@ -7,7 +7,7 @@
</head>
<body>
<div>
<img src="1280px-Morse-code-tree.svg.png">
<img src="code-tree.png">
</div>
<div>