mirror of
https://github.com/tendermint/tendermint.git
synced 2026-08-15 11:46:11 +00:00
Tendermint <-> Application refactor
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
tmsp "github.com/tendermint/tmsp/types"
|
||||
)
|
||||
|
||||
type Callback func(tmsp.Request, tmsp.Response)
|
||||
|
||||
type AppContext interface {
|
||||
SetResponseCallback(Callback)
|
||||
Error() error
|
||||
|
||||
EchoAsync(msg string)
|
||||
FlushAsync()
|
||||
AppendTxAsync(tx []byte)
|
||||
GetHashAsync()
|
||||
CommitAsync()
|
||||
RollbackAsync()
|
||||
SetOptionAsync(key string, value string)
|
||||
AddListenerAsync(key string)
|
||||
RemListenerAsync(key string)
|
||||
|
||||
InfoSync() (info []string, err error)
|
||||
FlushSync() error
|
||||
GetHashSync() (hash []byte, err error)
|
||||
CommitSync() error
|
||||
RollbackSync() error
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
tmsp "github.com/tendermint/tmsp/types"
|
||||
)
|
||||
|
||||
type localAppContext struct {
|
||||
tmsp.AppContext
|
||||
Callback
|
||||
}
|
||||
|
||||
func NewLocalAppContext(app tmsp.AppContext) *localAppContext {
|
||||
return &localAppContext{
|
||||
AppContext: app,
|
||||
}
|
||||
}
|
||||
|
||||
func (app *localAppContext) SetResponseCallback(cb Callback) {
|
||||
app.Callback = cb
|
||||
}
|
||||
|
||||
// TODO: change tmsp.AppContext to include Error()?
|
||||
func (app *localAppContext) Error() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (app *localAppContext) EchoAsync(msg string) {
|
||||
msg2 := app.AppContext.Echo(msg)
|
||||
app.Callback(
|
||||
tmsp.RequestEcho{msg},
|
||||
tmsp.ResponseEcho{msg2},
|
||||
)
|
||||
}
|
||||
|
||||
func (app *localAppContext) FlushAsync() {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
func (app *localAppContext) SetOptionAsync(key string, value string) {
|
||||
retCode := app.AppContext.SetOption(key, value)
|
||||
app.Callback(
|
||||
tmsp.RequestSetOption{key, value},
|
||||
tmsp.ResponseSetOption{retCode},
|
||||
)
|
||||
}
|
||||
|
||||
func (app *localAppContext) AppendTxAsync(tx []byte) {
|
||||
events, retCode := app.AppContext.AppendTx(tx)
|
||||
app.Callback(
|
||||
tmsp.RequestAppendTx{tx},
|
||||
tmsp.ResponseAppendTx{retCode},
|
||||
)
|
||||
for _, event := range events {
|
||||
app.Callback(
|
||||
nil,
|
||||
tmsp.ResponseEvent{event},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func (app *localAppContext) GetHashAsync() {
|
||||
hash, retCode := app.AppContext.GetHash()
|
||||
app.Callback(
|
||||
tmsp.RequestGetHash{},
|
||||
tmsp.ResponseGetHash{retCode, hash},
|
||||
)
|
||||
}
|
||||
|
||||
func (app *localAppContext) CommitAsync() {
|
||||
retCode := app.AppContext.Commit()
|
||||
app.Callback(
|
||||
tmsp.RequestCommit{},
|
||||
tmsp.ResponseCommit{retCode},
|
||||
)
|
||||
}
|
||||
|
||||
func (app *localAppContext) RollbackAsync() {
|
||||
retCode := app.AppContext.Rollback()
|
||||
app.Callback(
|
||||
tmsp.RequestRollback{},
|
||||
tmsp.ResponseRollback{retCode},
|
||||
)
|
||||
}
|
||||
|
||||
func (app *localAppContext) AddListenerAsync(key string) {
|
||||
retCode := app.AppContext.AddListener(key)
|
||||
app.Callback(
|
||||
tmsp.RequestAddListener{key},
|
||||
tmsp.ResponseAddListener{retCode},
|
||||
)
|
||||
}
|
||||
|
||||
func (app *localAppContext) RemListenerAsync(key string) {
|
||||
retCode := app.AppContext.RemListener(key)
|
||||
app.Callback(
|
||||
tmsp.RequestRemListener{key},
|
||||
tmsp.ResponseRemListener{retCode},
|
||||
)
|
||||
}
|
||||
|
||||
func (app *localAppContext) InfoSync() (info []string, err error) {
|
||||
info = app.AppContext.Info()
|
||||
return info, nil
|
||||
}
|
||||
|
||||
func (app *localAppContext) FlushSync() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (app *localAppContext) GetHashSync() (hash []byte, err error) {
|
||||
hash, retCode := app.AppContext.GetHash()
|
||||
return hash, retCode.Error()
|
||||
}
|
||||
|
||||
func (app *localAppContext) CommitSync() (err error) {
|
||||
retCode := app.AppContext.Commit()
|
||||
return retCode.Error()
|
||||
}
|
||||
|
||||
func (app *localAppContext) RollbackSync() (err error) {
|
||||
retCode := app.AppContext.Rollback()
|
||||
return retCode.Error()
|
||||
}
|
||||
@@ -1,349 +0,0 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"container/list"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"reflect"
|
||||
"sync"
|
||||
|
||||
. "github.com/tendermint/go-common"
|
||||
"github.com/tendermint/go-wire"
|
||||
tmsp "github.com/tendermint/tmsp/types"
|
||||
)
|
||||
|
||||
const maxResponseSize = 1048576 // 1MB
|
||||
|
||||
// This is goroutine-safe, but users should beware that
|
||||
// the application in general is not meant to be interfaced
|
||||
// with concurrent callers.
|
||||
// In other words, the mempool and consensus modules need to
|
||||
// exclude each other w/ an external mutex.
|
||||
type ProxyApp struct {
|
||||
QuitService
|
||||
sync.Mutex
|
||||
|
||||
reqQueue chan QueuedRequest
|
||||
|
||||
mtx sync.Mutex
|
||||
conn net.Conn
|
||||
bufWriter *bufio.Writer
|
||||
err error
|
||||
reqSent *list.List
|
||||
reqPending *list.Element // Next element in reqSent waiting for response
|
||||
resReceived *list.List
|
||||
eventsReceived *list.List
|
||||
}
|
||||
|
||||
func NewProxyApp(conn net.Conn, bufferSize int) *ProxyApp {
|
||||
p := &ProxyApp{
|
||||
reqQueue: make(chan QueuedRequest, bufferSize),
|
||||
conn: conn,
|
||||
bufWriter: bufio.NewWriter(conn),
|
||||
reqSent: list.New(),
|
||||
reqPending: nil,
|
||||
resReceived: list.New(),
|
||||
eventsReceived: list.New(),
|
||||
}
|
||||
p.QuitService = *NewQuitService(nil, "ProxyApp", p)
|
||||
return p
|
||||
}
|
||||
|
||||
func (p *ProxyApp) OnStart() error {
|
||||
p.QuitService.OnStart()
|
||||
go p.sendRequestsRoutine()
|
||||
go p.recvResponseRoutine()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *ProxyApp) OnStop() {
|
||||
p.QuitService.OnStop()
|
||||
p.conn.Close()
|
||||
}
|
||||
|
||||
func (p *ProxyApp) StopForError(err error) {
|
||||
p.mtx.Lock()
|
||||
fmt.Println("Stopping ProxyApp for error:", err)
|
||||
if p.err == nil {
|
||||
p.err = err
|
||||
}
|
||||
p.mtx.Unlock()
|
||||
p.Stop()
|
||||
}
|
||||
|
||||
func (p *ProxyApp) Error() error {
|
||||
p.mtx.Lock()
|
||||
defer p.mtx.Unlock()
|
||||
return p.err
|
||||
}
|
||||
|
||||
//----------------------------------------
|
||||
|
||||
func (p *ProxyApp) sendRequestsRoutine() {
|
||||
for {
|
||||
var n int
|
||||
var err error
|
||||
select {
|
||||
case <-p.QuitService.Quit:
|
||||
return
|
||||
case qreq := <-p.reqQueue:
|
||||
wire.WriteBinary(qreq.Request, p.bufWriter, &n, &err)
|
||||
if err != nil {
|
||||
p.StopForError(err)
|
||||
return
|
||||
}
|
||||
if _, ok := qreq.Request.(tmsp.RequestFlush); ok {
|
||||
err = p.bufWriter.Flush()
|
||||
if err != nil {
|
||||
p.StopForError(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
p.didSendReq(qreq)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *ProxyApp) recvResponseRoutine() {
|
||||
r := bufio.NewReader(p.conn) // Buffer reads
|
||||
for {
|
||||
var res tmsp.Response
|
||||
var n int
|
||||
var err error
|
||||
wire.ReadBinaryPtr(&res, r, maxResponseSize, &n, &err)
|
||||
if err != nil {
|
||||
p.StopForError(err)
|
||||
return
|
||||
}
|
||||
switch res := res.(type) {
|
||||
case tmsp.ResponseException:
|
||||
p.StopForError(errors.New(res.Error))
|
||||
case tmsp.ResponseEvent:
|
||||
p.didRecvEvent(res.Event)
|
||||
default:
|
||||
err := p.didRecvResponse(res)
|
||||
if err != nil {
|
||||
p.StopForError(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *ProxyApp) didSendReq(qreq QueuedRequest) {
|
||||
p.mtx.Lock()
|
||||
defer p.mtx.Unlock()
|
||||
|
||||
p.reqSent.PushBack(qreq)
|
||||
if p.reqPending == nil {
|
||||
p.reqPending = p.reqSent.Front()
|
||||
}
|
||||
}
|
||||
|
||||
func (p *ProxyApp) didRecvResponse(res tmsp.Response) error {
|
||||
p.mtx.Lock()
|
||||
defer p.mtx.Unlock()
|
||||
|
||||
if p.reqPending == nil {
|
||||
return fmt.Errorf("Unexpected result type %v when nothing expected",
|
||||
reflect.TypeOf(res))
|
||||
} else {
|
||||
qreq := p.reqPending.Value.(QueuedRequest)
|
||||
if !resMatchesReq(qreq.Request, res) {
|
||||
return fmt.Errorf("Unexpected result type %v when response to %v expected",
|
||||
reflect.TypeOf(res), reflect.TypeOf(qreq.Request))
|
||||
}
|
||||
if qreq.Sync {
|
||||
qreq.Done()
|
||||
}
|
||||
p.reqPending = p.reqPending.Next()
|
||||
}
|
||||
p.resReceived.PushBack(res)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *ProxyApp) didRecvEvent(event tmsp.Event) {
|
||||
p.mtx.Lock()
|
||||
defer p.mtx.Unlock()
|
||||
|
||||
p.eventsReceived.PushBack(event)
|
||||
}
|
||||
|
||||
//----------------------------------------
|
||||
|
||||
func (p *ProxyApp) EchoAsync(key string) {
|
||||
p.queueRequestAsync(tmsp.RequestEcho{key})
|
||||
}
|
||||
|
||||
func (p *ProxyApp) FlushAsync() {
|
||||
p.queueRequestAsync(tmsp.RequestFlush{})
|
||||
}
|
||||
|
||||
func (p *ProxyApp) AppendTxAsync(tx []byte) {
|
||||
p.queueRequestAsync(tmsp.RequestAppendTx{tx})
|
||||
}
|
||||
|
||||
func (p *ProxyApp) GetHashAsync() {
|
||||
p.queueRequestAsync(tmsp.RequestGetHash{})
|
||||
}
|
||||
|
||||
/*
|
||||
func (p *ProxyApp) CommitAsync() {
|
||||
p.queueRequestAsync(tmsp.RequestCommit{})
|
||||
}
|
||||
|
||||
func (p *ProxyApp) RollbackAsync() {
|
||||
p.queueRequestAsync(tmsp.RequestRollback{})
|
||||
}
|
||||
*/
|
||||
|
||||
func (p *ProxyApp) SetEventsModeAsync(mode tmsp.EventsMode) {
|
||||
p.queueRequestAsync(tmsp.RequestSetEventsMode{mode})
|
||||
}
|
||||
|
||||
func (p *ProxyApp) AddListenerAsync(key string) {
|
||||
p.queueRequestAsync(tmsp.RequestAddListener{key})
|
||||
}
|
||||
|
||||
func (p *ProxyApp) RemListenerAsync(key string) {
|
||||
p.queueRequestAsync(tmsp.RequestRemListener{key})
|
||||
}
|
||||
|
||||
//----------------------------------------
|
||||
|
||||
// Get valid txs, root hash, events; or error
|
||||
// Clears internal buffers
|
||||
func (p *ProxyApp) ReapSync(commit bool) (txs [][]byte, hash []byte, events []tmsp.Event, err error) {
|
||||
if commit {
|
||||
// Send asynchronous commit
|
||||
p.queueRequestAsync(tmsp.RequestCommit{})
|
||||
// NOTE: we're assuming that there won't be a race condition.
|
||||
}
|
||||
// Get hash.
|
||||
p.queueRequestAsync(tmsp.RequestGetHash{})
|
||||
// Flush everything.
|
||||
p.queueRequestSync(tmsp.RequestFlush{})
|
||||
// Maybe there was an error in response matching
|
||||
if p.err != nil {
|
||||
return nil, nil, nil, p.err
|
||||
}
|
||||
// Process the resReceived/reqSent/reqPending.
|
||||
if p.resReceived.Len() != p.reqSent.Len() {
|
||||
PanicSanity("Unmatched requests & responses")
|
||||
}
|
||||
var commitCounter = 0
|
||||
txs = make([][]byte, 0, p.reqSent.Len())
|
||||
events = make([]tmsp.Event, 0, p.eventsReceived.Len())
|
||||
reqE, resE := p.reqSent.Front(), p.resReceived.Front()
|
||||
for ; reqE != nil; reqE, resE = reqE.Next(), resE.Next() {
|
||||
req, res := reqE.Value.(tmsp.Request), resE.Value.(tmsp.Response)
|
||||
switch req := req.(type) {
|
||||
case tmsp.RequestAppendTx:
|
||||
txs = append(txs, req.TxBytes)
|
||||
case tmsp.RequestGetHash:
|
||||
hash = res.(tmsp.ResponseGetHash).Hash
|
||||
case tmsp.RequestCommit:
|
||||
if commitCounter > 0 {
|
||||
PanicSanity("Unexpected Commit response")
|
||||
}
|
||||
commitCounter++
|
||||
case tmsp.RequestRollback:
|
||||
PanicSanity("Unexpected Rollback response")
|
||||
default:
|
||||
// ignore other messages
|
||||
}
|
||||
}
|
||||
for eE := p.eventsReceived.Front(); eE != nil; eE = eE.Next() {
|
||||
events = append(events, eE.Value.(tmsp.Event))
|
||||
}
|
||||
|
||||
return txs, hash, events, nil
|
||||
}
|
||||
|
||||
// Rollback or error
|
||||
// Clears internal buffers
|
||||
func (p *ProxyApp) RollbackSync() (err error) {
|
||||
// Get hash.
|
||||
p.queueRequestAsync(tmsp.RequestRollback{})
|
||||
// Flush everything.
|
||||
p.queueRequestSync(tmsp.RequestFlush{})
|
||||
// Maybe there was an error in response matching
|
||||
if p.err != nil {
|
||||
return p.err
|
||||
}
|
||||
p.reqSent = list.New()
|
||||
p.reqPending = nil
|
||||
p.resReceived = list.New()
|
||||
p.eventsReceived = list.New()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *ProxyApp) InfoSync() []string {
|
||||
p.queueRequestAsync(tmsp.RequestInfo{})
|
||||
p.queueRequestSync(tmsp.RequestFlush{})
|
||||
return p.resReceived.Back().Prev().Value.(tmsp.ResponseInfo).Data
|
||||
}
|
||||
|
||||
func (p *ProxyApp) FlushSync() {
|
||||
p.queueRequestSync(tmsp.RequestFlush{})
|
||||
}
|
||||
|
||||
//----------------------------------------
|
||||
|
||||
func (p *ProxyApp) queueRequestAsync(req tmsp.Request) {
|
||||
qreq := QueuedRequest{Request: req}
|
||||
p.reqQueue <- qreq
|
||||
}
|
||||
|
||||
func (p *ProxyApp) queueRequestSync(req tmsp.Request) {
|
||||
qreq := QueuedRequest{
|
||||
req,
|
||||
true,
|
||||
waitGroup1(),
|
||||
}
|
||||
p.reqQueue <- qreq
|
||||
qreq.Wait()
|
||||
}
|
||||
|
||||
//----------------------------------------
|
||||
|
||||
func waitGroup1() (wg *sync.WaitGroup) {
|
||||
wg = &sync.WaitGroup{}
|
||||
wg.Add(1)
|
||||
return
|
||||
}
|
||||
|
||||
func resMatchesReq(req tmsp.Request, res tmsp.Response) (ok bool) {
|
||||
switch req.(type) {
|
||||
case tmsp.RequestEcho:
|
||||
_, ok = res.(tmsp.ResponseEcho)
|
||||
case tmsp.RequestFlush:
|
||||
_, ok = res.(tmsp.ResponseFlush)
|
||||
case tmsp.RequestInfo:
|
||||
_, ok = res.(tmsp.ResponseInfo)
|
||||
case tmsp.RequestAppendTx:
|
||||
_, ok = res.(tmsp.ResponseAppendTx)
|
||||
case tmsp.RequestGetHash:
|
||||
_, ok = res.(tmsp.ResponseGetHash)
|
||||
case tmsp.RequestCommit:
|
||||
_, ok = res.(tmsp.ResponseCommit)
|
||||
case tmsp.RequestRollback:
|
||||
_, ok = res.(tmsp.ResponseRollback)
|
||||
case tmsp.RequestSetEventsMode:
|
||||
_, ok = res.(tmsp.ResponseSetEventsMode)
|
||||
case tmsp.RequestAddListener:
|
||||
_, ok = res.(tmsp.ResponseAddListener)
|
||||
case tmsp.RequestRemListener:
|
||||
_, ok = res.(tmsp.ResponseRemListener)
|
||||
default:
|
||||
return false
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type QueuedRequest struct {
|
||||
tmsp.Request
|
||||
Sync bool
|
||||
*sync.WaitGroup
|
||||
}
|
||||
@@ -0,0 +1,306 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"container/list"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"reflect"
|
||||
"sync"
|
||||
|
||||
. "github.com/tendermint/go-common"
|
||||
"github.com/tendermint/go-wire"
|
||||
tmsp "github.com/tendermint/tmsp/types"
|
||||
)
|
||||
|
||||
const maxResponseSize = 1048576 // 1MB
|
||||
|
||||
// This is goroutine-safe, but users should beware that
|
||||
// the application in general is not meant to be interfaced
|
||||
// with concurrent callers.
|
||||
type remoteAppContext struct {
|
||||
QuitService
|
||||
sync.Mutex
|
||||
|
||||
reqQueue chan *reqRes
|
||||
|
||||
mtx sync.Mutex
|
||||
conn net.Conn
|
||||
bufWriter *bufio.Writer
|
||||
err error
|
||||
reqSent *list.List
|
||||
resCb func(tmsp.Request, tmsp.Response)
|
||||
}
|
||||
|
||||
func NewRemoteAppContext(conn net.Conn, bufferSize int) *remoteAppContext {
|
||||
app := &remoteAppContext{
|
||||
reqQueue: make(chan *reqRes, bufferSize),
|
||||
conn: conn,
|
||||
bufWriter: bufio.NewWriter(conn),
|
||||
reqSent: list.New(),
|
||||
resCb: nil,
|
||||
}
|
||||
app.QuitService = *NewQuitService(nil, "remoteAppContext", app)
|
||||
return app
|
||||
}
|
||||
|
||||
func (app *remoteAppContext) OnStart() error {
|
||||
app.QuitService.OnStart()
|
||||
go app.sendRequestsRoutine()
|
||||
go app.recvResponseRoutine()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (app *remoteAppContext) OnStop() {
|
||||
app.QuitService.OnStop()
|
||||
app.conn.Close()
|
||||
}
|
||||
|
||||
func (app *remoteAppContext) SetResponseCallback(resCb Callback) {
|
||||
app.mtx.Lock()
|
||||
defer app.mtx.Unlock()
|
||||
app.resCb = resCb
|
||||
}
|
||||
|
||||
func (app *remoteAppContext) StopForError(err error) {
|
||||
app.mtx.Lock()
|
||||
fmt.Println("Stopping remoteAppContext for error:", err)
|
||||
if app.err == nil {
|
||||
app.err = err
|
||||
}
|
||||
app.mtx.Unlock()
|
||||
app.Stop()
|
||||
}
|
||||
|
||||
func (app *remoteAppContext) Error() error {
|
||||
app.mtx.Lock()
|
||||
defer app.mtx.Unlock()
|
||||
return app.err
|
||||
}
|
||||
|
||||
//----------------------------------------
|
||||
|
||||
func (app *remoteAppContext) sendRequestsRoutine() {
|
||||
for {
|
||||
var n int
|
||||
var err error
|
||||
select {
|
||||
case <-app.QuitService.Quit:
|
||||
return
|
||||
case reqres := <-app.reqQueue:
|
||||
wire.WriteBinary(reqres.Request, app.bufWriter, &n, &err)
|
||||
if err != nil {
|
||||
app.StopForError(err)
|
||||
return
|
||||
}
|
||||
if _, ok := reqres.Request.(tmsp.RequestFlush); ok {
|
||||
err = app.bufWriter.Flush()
|
||||
if err != nil {
|
||||
app.StopForError(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
app.didSendReq(reqres)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (app *remoteAppContext) recvResponseRoutine() {
|
||||
r := bufio.NewReader(app.conn) // Buffer reads
|
||||
for {
|
||||
var res tmsp.Response
|
||||
var n int
|
||||
var err error
|
||||
wire.ReadBinaryPtr(&res, r, maxResponseSize, &n, &err)
|
||||
if err != nil {
|
||||
app.StopForError(err)
|
||||
return
|
||||
}
|
||||
switch res := res.(type) {
|
||||
case tmsp.ResponseException:
|
||||
app.StopForError(errors.New(res.Error))
|
||||
default:
|
||||
err := app.didRecvResponse(res)
|
||||
if err != nil {
|
||||
app.StopForError(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (app *remoteAppContext) didSendReq(reqres *reqRes) {
|
||||
app.mtx.Lock()
|
||||
defer app.mtx.Unlock()
|
||||
app.reqSent.PushBack(reqres)
|
||||
}
|
||||
|
||||
func (app *remoteAppContext) didRecvResponse(res tmsp.Response) error {
|
||||
app.mtx.Lock()
|
||||
defer app.mtx.Unlock()
|
||||
|
||||
// Special logic for events which have no corresponding requests.
|
||||
if _, ok := res.(tmsp.ResponseEvent); ok && app.resCb != nil {
|
||||
app.resCb(nil, res)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get the first reqRes
|
||||
next := app.reqSent.Front()
|
||||
if next == nil {
|
||||
return fmt.Errorf("Unexpected result type %v when nothing expected", reflect.TypeOf(res))
|
||||
}
|
||||
reqres := next.Value.(*reqRes)
|
||||
if !resMatchesReq(reqres.Request, res) {
|
||||
return fmt.Errorf("Unexpected result type %v when response to %v expected",
|
||||
reflect.TypeOf(res), reflect.TypeOf(reqres.Request))
|
||||
}
|
||||
|
||||
reqres.Response = res // Set response
|
||||
reqres.Done() // Release waiters
|
||||
app.reqSent.Remove(next) // Pop first item from linked list
|
||||
|
||||
// Callback if there is a listener
|
||||
if app.resCb != nil {
|
||||
app.resCb(reqres.Request, res)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
//----------------------------------------
|
||||
|
||||
func (app *remoteAppContext) EchoAsync(msg string) {
|
||||
app.queueRequest(tmsp.RequestEcho{msg})
|
||||
}
|
||||
|
||||
func (app *remoteAppContext) FlushAsync() {
|
||||
app.queueRequest(tmsp.RequestFlush{})
|
||||
}
|
||||
|
||||
func (app *remoteAppContext) SetOptionAsync(key string, value string) {
|
||||
app.queueRequest(tmsp.RequestSetOption{key, value})
|
||||
}
|
||||
|
||||
func (app *remoteAppContext) AppendTxAsync(tx []byte) {
|
||||
app.queueRequest(tmsp.RequestAppendTx{tx})
|
||||
}
|
||||
|
||||
func (app *remoteAppContext) GetHashAsync() {
|
||||
app.queueRequest(tmsp.RequestGetHash{})
|
||||
}
|
||||
|
||||
func (app *remoteAppContext) CommitAsync() {
|
||||
app.queueRequest(tmsp.RequestCommit{})
|
||||
}
|
||||
|
||||
func (app *remoteAppContext) RollbackAsync() {
|
||||
app.queueRequest(tmsp.RequestRollback{})
|
||||
}
|
||||
|
||||
func (app *remoteAppContext) AddListenerAsync(key string) {
|
||||
app.queueRequest(tmsp.RequestAddListener{key})
|
||||
}
|
||||
|
||||
func (app *remoteAppContext) RemListenerAsync(key string) {
|
||||
app.queueRequest(tmsp.RequestRemListener{key})
|
||||
}
|
||||
|
||||
//----------------------------------------
|
||||
|
||||
func (app *remoteAppContext) InfoSync() (info []string, err error) {
|
||||
reqres := app.queueRequest(tmsp.RequestInfo{})
|
||||
app.FlushSync()
|
||||
if app.err != nil {
|
||||
return nil, app.err
|
||||
}
|
||||
return reqres.Response.(tmsp.ResponseInfo).Data, nil
|
||||
}
|
||||
|
||||
func (app *remoteAppContext) FlushSync() error {
|
||||
app.queueRequest(tmsp.RequestFlush{}).Wait()
|
||||
return app.err
|
||||
}
|
||||
|
||||
func (app *remoteAppContext) GetHashSync() (hash []byte, err error) {
|
||||
reqres := app.queueRequest(tmsp.RequestGetHash{})
|
||||
app.FlushSync()
|
||||
if app.err != nil {
|
||||
return nil, app.err
|
||||
}
|
||||
return reqres.Response.(tmsp.ResponseGetHash).Hash, nil
|
||||
}
|
||||
|
||||
// Commits or error
|
||||
func (app *remoteAppContext) CommitSync() (err error) {
|
||||
app.queueRequest(tmsp.RequestCommit{})
|
||||
app.FlushSync()
|
||||
return app.err
|
||||
}
|
||||
|
||||
// Rollback or error
|
||||
// Clears internal buffers
|
||||
func (app *remoteAppContext) RollbackSync() (err error) {
|
||||
app.queueRequest(tmsp.RequestRollback{})
|
||||
app.FlushSync()
|
||||
return app.err
|
||||
}
|
||||
|
||||
//----------------------------------------
|
||||
|
||||
func (app *remoteAppContext) queueRequest(req tmsp.Request) *reqRes {
|
||||
reqres := NewreqRes(req)
|
||||
// TODO: set app.err if reqQueue times out
|
||||
app.reqQueue <- reqres
|
||||
return reqres
|
||||
}
|
||||
|
||||
//----------------------------------------
|
||||
|
||||
func resMatchesReq(req tmsp.Request, res tmsp.Response) (ok bool) {
|
||||
switch req.(type) {
|
||||
case tmsp.RequestEcho:
|
||||
_, ok = res.(tmsp.ResponseEcho)
|
||||
case tmsp.RequestFlush:
|
||||
_, ok = res.(tmsp.ResponseFlush)
|
||||
case tmsp.RequestInfo:
|
||||
_, ok = res.(tmsp.ResponseInfo)
|
||||
case tmsp.RequestSetOption:
|
||||
_, ok = res.(tmsp.ResponseSetOption)
|
||||
case tmsp.RequestAppendTx:
|
||||
_, ok = res.(tmsp.ResponseAppendTx)
|
||||
case tmsp.RequestGetHash:
|
||||
_, ok = res.(tmsp.ResponseGetHash)
|
||||
case tmsp.RequestCommit:
|
||||
_, ok = res.(tmsp.ResponseCommit)
|
||||
case tmsp.RequestRollback:
|
||||
_, ok = res.(tmsp.ResponseRollback)
|
||||
case tmsp.RequestAddListener:
|
||||
_, ok = res.(tmsp.ResponseAddListener)
|
||||
case tmsp.RequestRemListener:
|
||||
_, ok = res.(tmsp.ResponseRemListener)
|
||||
default:
|
||||
return false
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type reqRes struct {
|
||||
tmsp.Request
|
||||
*sync.WaitGroup
|
||||
tmsp.Response // Not set atomically, so be sure to use WaitGroup.
|
||||
}
|
||||
|
||||
func NewreqRes(req tmsp.Request) *reqRes {
|
||||
return &reqRes{
|
||||
Request: req,
|
||||
WaitGroup: waitGroup1(),
|
||||
Response: nil,
|
||||
}
|
||||
}
|
||||
|
||||
func waitGroup1() (wg *sync.WaitGroup) {
|
||||
wg = &sync.WaitGroup{}
|
||||
wg.Add(1)
|
||||
return
|
||||
}
|
||||
@@ -26,7 +26,8 @@ func TestEcho(t *testing.T) {
|
||||
|
||||
logBuffer := bytes.NewBuffer(nil)
|
||||
logConn := logio.NewLoggedConn(conn, logBuffer)
|
||||
proxy := NewProxyApp(logConn, 10)
|
||||
proxy := NewRemoteAppContext(logConn, 10)
|
||||
proxy.SetResponseCallback(nil)
|
||||
proxy.Start()
|
||||
|
||||
for i := 0; i < 1000; i++ {
|
||||
@@ -34,17 +35,11 @@ func TestEcho(t *testing.T) {
|
||||
}
|
||||
proxy.FlushSync()
|
||||
|
||||
if proxy.reqSent.Len() != 1001 {
|
||||
t.Error(Fmt("Expected 1001 requests sent, got %v",
|
||||
proxy.reqSent.Len()))
|
||||
}
|
||||
if proxy.resReceived.Len() != 1001 {
|
||||
t.Error(Fmt("Expected 1001 responses received, got %v",
|
||||
proxy.resReceived.Len()))
|
||||
}
|
||||
if t.Failed() {
|
||||
logio.PrintReader(logBuffer)
|
||||
}
|
||||
/*
|
||||
if t.Failed() {
|
||||
logio.PrintReader(logBuffer)
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
func BenchmarkEcho(b *testing.B) {
|
||||
@@ -61,7 +56,7 @@ func BenchmarkEcho(b *testing.B) {
|
||||
b.Log("Connected")
|
||||
}
|
||||
|
||||
proxy := NewProxyApp(conn, 10)
|
||||
proxy := NewRemoteAppContext(conn, 10)
|
||||
proxy.Start()
|
||||
echoString := strings.Repeat(" ", 200)
|
||||
b.StartTimer() // Start benchmarking tests
|
||||
@@ -91,10 +86,12 @@ func TestInfo(t *testing.T) {
|
||||
|
||||
logBuffer := bytes.NewBuffer(nil)
|
||||
logConn := logio.NewLoggedConn(conn, logBuffer)
|
||||
proxy := NewProxyApp(logConn, 10)
|
||||
proxy := NewRemoteAppContext(logConn, 10)
|
||||
proxy.Start()
|
||||
data := proxy.InfoSync()
|
||||
|
||||
data, err := proxy.InfoSync()
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
if data[0] != "size:0" {
|
||||
t.Error("Expected ResponseInfo with one element 'size:0' but got something else")
|
||||
}
|
||||
Reference in New Issue
Block a user