Skip to content

Commit c4fd59a

Browse files
committed
Implement Actor Ask (inspired by Akka)
1 parent a3272b5 commit c4fd59a

File tree

2 files changed

+136
-2
lines changed

2 files changed

+136
-2
lines changed

actor.go

Lines changed: 82 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ import (
44
"time"
55
)
66

7+
// ActorHandle A target could send messages
8+
type ActorHandle interface {
9+
Send(message interface{})
10+
}
11+
712
// ActorDef Actor model inspired by Erlang/Akka
813
type ActorDef struct {
914
id time.Time
@@ -102,8 +107,83 @@ func (actorSelf *ActorDef) run() {
102107
// Actor Actor utils instance
103108
var Actor ActorDef
104109

110+
// AskDef Ask inspired by Erlang/Akka
111+
type AskDef struct {
112+
id time.Time
113+
ch *chan interface{}
114+
timeout *time.Duration
115+
116+
Message interface{}
117+
}
118+
119+
// New New Ask instance
120+
func (askSelf *AskDef) New(message interface{}, timeout *time.Duration) *AskDef {
121+
return AskNewGenerics(message, timeout)
122+
}
123+
124+
// NewByOptions New Ask by its options
125+
func (askSelf *AskDef) NewByOptions(message interface{}, ioCh *chan interface{}, timeout *time.Duration) *AskDef {
126+
return AskNewByOptionsGenerics(message, ioCh, timeout)
127+
}
128+
129+
// AskNewGenerics New Ask instance
130+
func AskNewGenerics(message interface{}, timeout *time.Duration) *AskDef {
131+
ch := make(chan interface{})
132+
return AskNewByOptionsGenerics(message, &ch, timeout)
133+
}
134+
135+
// AskNewByOptionsGenerics New Ask by its options
136+
func AskNewByOptionsGenerics(message interface{}, ioCh *chan interface{}, timeout *time.Duration) *AskDef {
137+
newOne := AskDef{
138+
id: time.Now(),
139+
ch: ioCh,
140+
timeout: timeout,
141+
142+
Message: message,
143+
}
144+
145+
return &newOne
146+
}
147+
148+
// AskOnce Sender Ask
149+
func (askSelf *AskDef) AskOnce(target ActorHandle) interface{} {
150+
ch, timer := askSelf.AskChannel(target)
151+
result, success := <-*ch
152+
if success && timer != nil {
153+
timer.Stop()
154+
close(*ch)
155+
}
156+
157+
return result
158+
}
159+
160+
// AskChannel Sender Ask
161+
func (askSelf *AskDef) AskChannel(target ActorHandle) (*chan interface{}, *time.Timer) {
162+
var timer *time.Timer
163+
target.Send(askSelf)
164+
if askSelf.timeout != nil {
165+
timer = time.NewTimer(time.Second)
166+
go func() {
167+
<-timer.C
168+
close(*askSelf.ch)
169+
}()
170+
}
171+
172+
return askSelf.ch, timer
173+
}
174+
175+
// Reply Receiver Reply
176+
func (askSelf *AskDef) Reply(response interface{}) {
177+
*askSelf.ch <- response
178+
}
179+
180+
// Ask Ask utils instance
181+
var Ask AskDef
182+
105183
func init() {
106-
Actor = *Actor.New(func(_ *ActorDef, _ interface{}) {})
107-
Actor.Close()
184+
//Ask = *Ask.New(0, nil)
185+
//Actor = *Actor.New(func(_ *ActorDef, _ interface{}) {})
186+
//Actor.Close()
187+
Actor.isClosed = true
108188
defaultActor = &Actor
109189
}

actor_test.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package fpgo
22

33
import (
44
"testing"
5+
"time"
56

67
"github.com/stretchr/testify/assert"
78
)
@@ -82,3 +83,56 @@ func TestActorCommon(t *testing.T) {
8283
assert.Equal(t, expectedInt, actual)
8384
}
8485
}
86+
87+
func TestActorAsk(t *testing.T) {
88+
var expectedInt int
89+
var actual int
90+
91+
// Testee
92+
actorRoot := Actor.New(func(self *ActorDef, input interface{}) {
93+
// Ask cases: ROOT
94+
switch val := input.(type) {
95+
case *AskDef:
96+
intVal, _ := Maybe.Just(val.Message).ToInt()
97+
98+
// NOTE If negative, hanging for testing Ask.timeout
99+
if intVal < 0 {
100+
break
101+
}
102+
103+
val.Reply(intVal * 10)
104+
break
105+
}
106+
})
107+
108+
// var timer *time.Timer
109+
var timeout time.Duration
110+
timeout = 10 * time.Millisecond
111+
112+
// Normal cases
113+
actual = 0
114+
expectedInt = 10
115+
actual, _ = Maybe.Just(AskNewGenerics(1, nil).AskOnce(actorRoot)).ToInt()
116+
assert.Equal(t, expectedInt, actual)
117+
// Ask with Timeout
118+
actual = 0
119+
expectedInt = 20
120+
actual, _ = Maybe.Just(AskNewGenerics(2, &timeout).AskOnce(actorRoot)).ToInt()
121+
assert.Equal(t, expectedInt, actual)
122+
// Ask channel
123+
actual = 0
124+
expectedInt = 30
125+
ch, timer := AskNewGenerics(3, &timeout).AskChannel(actorRoot)
126+
actual, _ = Maybe.Just(<-*ch).ToInt()
127+
close(*ch)
128+
if timer != nil {
129+
timer.Stop()
130+
}
131+
assert.Equal(t, expectedInt, actual)
132+
133+
// Timeout cases
134+
actual = 9999
135+
expectedInt = 0
136+
actual, _ = Maybe.Just(AskNewGenerics(-1, &timeout).AskOnce(actorRoot)).ToInt()
137+
assert.Equal(t, expectedInt, actual)
138+
}

0 commit comments

Comments
 (0)