mirror of
https://github.com/fluencelabs/tendermint
synced 2025-06-28 12:11:44 +00:00
Refactor Application to Application & AppContext
This commit is contained in:
@ -66,7 +66,7 @@ func cmdAppendTx(c *cli.Context) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
Exit(err.Error())
|
Exit(err.Error())
|
||||||
}
|
}
|
||||||
res, err := write(conn, types.RequestAppendTx{[]byte(args[0])})
|
res, err := makeRequest(conn, types.RequestAppendTx{[]byte(args[0])})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Exit(err.Error())
|
Exit(err.Error())
|
||||||
}
|
}
|
||||||
@ -79,7 +79,7 @@ func cmdGetHash(c *cli.Context) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
Exit(err.Error())
|
Exit(err.Error())
|
||||||
}
|
}
|
||||||
res, err := write(conn, types.RequestGetHash{})
|
res, err := makeRequest(conn, types.RequestGetHash{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Exit(err.Error())
|
Exit(err.Error())
|
||||||
}
|
}
|
||||||
@ -92,7 +92,7 @@ func cmdCommit(c *cli.Context) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
Exit(err.Error())
|
Exit(err.Error())
|
||||||
}
|
}
|
||||||
_, err = write(conn, types.RequestCommit{})
|
_, err = makeRequest(conn, types.RequestCommit{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Exit(err.Error())
|
Exit(err.Error())
|
||||||
}
|
}
|
||||||
@ -105,7 +105,7 @@ func cmdRollback(c *cli.Context) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
Exit(err.Error())
|
Exit(err.Error())
|
||||||
}
|
}
|
||||||
_, err = write(conn, types.RequestRollback{})
|
_, err = makeRequest(conn, types.RequestRollback{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Exit(err.Error())
|
Exit(err.Error())
|
||||||
}
|
}
|
||||||
@ -114,21 +114,35 @@ func cmdRollback(c *cli.Context) {
|
|||||||
|
|
||||||
//--------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------
|
||||||
|
|
||||||
func write(conn net.Conn, req types.Request) (types.Response, error) {
|
func makeRequest(conn net.Conn, req types.Request) (types.Response, error) {
|
||||||
var n int
|
var n int
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
|
// Write desired request
|
||||||
wire.WriteBinary(req, conn, &n, &err)
|
wire.WriteBinary(req, conn, &n, &err)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// flush!
|
// Write flush request
|
||||||
wire.WriteBinary(types.RequestFlush{}, conn, &n, &err)
|
wire.WriteBinary(types.RequestFlush{}, conn, &n, &err)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Read desired response
|
||||||
var res types.Response
|
var res types.Response
|
||||||
wire.ReadBinaryPtr(&res, conn, 0, &n, &err)
|
wire.ReadBinaryPtr(&res, conn, 0, &n, &err)
|
||||||
return res, err
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read flush response
|
||||||
|
var resFlush types.ResponseFlush
|
||||||
|
wire.ReadBinaryPtr(&resFlush, conn, 0, &n, &err)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return res, nil
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package example
|
package example
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
. "github.com/tendermint/go-common"
|
. "github.com/tendermint/go-common"
|
||||||
"github.com/tendermint/go-merkle"
|
"github.com/tendermint/go-merkle"
|
||||||
"github.com/tendermint/go-wire"
|
"github.com/tendermint/go-wire"
|
||||||
@ -8,8 +10,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type DummyApplication struct {
|
type DummyApplication struct {
|
||||||
state merkle.Tree
|
mtx sync.Mutex
|
||||||
lastCommitState merkle.Tree
|
state merkle.Tree
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDummyApplication() *DummyApplication {
|
func NewDummyApplication() *DummyApplication {
|
||||||
@ -19,48 +21,77 @@ func NewDummyApplication() *DummyApplication {
|
|||||||
0,
|
0,
|
||||||
nil,
|
nil,
|
||||||
)
|
)
|
||||||
return &DummyApplication{
|
return &DummyApplication{state: state}
|
||||||
state: state,
|
}
|
||||||
lastCommitState: state,
|
|
||||||
|
func (dapp *DummyApplication) Open() types.AppContext {
|
||||||
|
dapp.mtx.Lock()
|
||||||
|
defer dapp.mtx.Unlock()
|
||||||
|
return &DummyAppContext{
|
||||||
|
app: dapp,
|
||||||
|
state: dapp.state.Copy(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dapp *DummyApplication) Echo(message string) string {
|
func (dapp *DummyApplication) commitState(state merkle.Tree) {
|
||||||
|
dapp.mtx.Lock()
|
||||||
|
defer dapp.mtx.Unlock()
|
||||||
|
dapp.state = state.Copy()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dapp *DummyApplication) getState() merkle.Tree {
|
||||||
|
dapp.mtx.Lock()
|
||||||
|
defer dapp.mtx.Unlock()
|
||||||
|
return dapp.state.Copy()
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
type DummyAppContext struct {
|
||||||
|
app *DummyApplication
|
||||||
|
state merkle.Tree
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dac *DummyAppContext) Echo(message string) string {
|
||||||
return message
|
return message
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dapp *DummyApplication) Info() []string {
|
func (dac *DummyAppContext) Info() []string {
|
||||||
return []string{Fmt("size:%v", dapp.state.Size())}
|
return []string{Fmt("size:%v", dac.state.Size())}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dapp *DummyApplication) SetOption(key string, value string) types.RetCode {
|
func (dac *DummyAppContext) SetOption(key string, value string) types.RetCode {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dapp *DummyApplication) AppendTx(tx []byte) ([]types.Event, types.RetCode) {
|
func (dac *DummyAppContext) AppendTx(tx []byte) ([]types.Event, types.RetCode) {
|
||||||
dapp.state.Set(tx, tx)
|
dac.state.Set(tx, tx)
|
||||||
return nil, 0
|
return nil, 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dapp *DummyApplication) GetHash() ([]byte, types.RetCode) {
|
func (dac *DummyAppContext) GetHash() ([]byte, types.RetCode) {
|
||||||
hash := dapp.state.Hash()
|
hash := dac.state.Hash()
|
||||||
return hash, 0
|
return hash, 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dapp *DummyApplication) Commit() types.RetCode {
|
func (dac *DummyAppContext) Commit() types.RetCode {
|
||||||
dapp.lastCommitState = dapp.state.Copy()
|
dac.app.commitState(dac.state)
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dapp *DummyApplication) Rollback() types.RetCode {
|
func (dac *DummyAppContext) Rollback() types.RetCode {
|
||||||
dapp.state = dapp.lastCommitState.Copy()
|
dac.state = dac.app.getState()
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dapp *DummyApplication) AddListener(key string) types.RetCode {
|
func (dac *DummyAppContext) AddListener(key string) types.RetCode {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dapp *DummyApplication) RemListener(key string) types.RetCode {
|
func (dac *DummyAppContext) RemListener(key string) types.RetCode {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (dac *DummyAppContext) Close() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
package example
|
package example
|
||||||
|
|
||||||
import (
|
import (
|
||||||
// "fmt"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
. "github.com/tendermint/go-common"
|
. "github.com/tendermint/go-common"
|
||||||
"github.com/tendermint/go-wire"
|
"github.com/tendermint/go-wire"
|
||||||
@ -12,6 +13,8 @@ import (
|
|||||||
|
|
||||||
func TestStream(t *testing.T) {
|
func TestStream(t *testing.T) {
|
||||||
|
|
||||||
|
numAppendTxs := 200000
|
||||||
|
|
||||||
// Start the listener
|
// Start the listener
|
||||||
_, err := server.StartListener("tcp://127.0.0.1:8080", NewDummyApplication())
|
_, err := server.StartListener("tcp://127.0.0.1:8080", NewDummyApplication())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -25,7 +28,9 @@ func TestStream(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Read response data
|
// Read response data
|
||||||
|
done := make(chan struct{})
|
||||||
go func() {
|
go func() {
|
||||||
|
counter := 0
|
||||||
for {
|
for {
|
||||||
var n int
|
var n int
|
||||||
var err error
|
var err error
|
||||||
@ -34,19 +39,59 @@ func TestStream(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
Exit(err.Error())
|
Exit(err.Error())
|
||||||
}
|
}
|
||||||
// fmt.Println("Read", n)
|
|
||||||
|
// Process response
|
||||||
|
switch res := res.(type) {
|
||||||
|
case types.ResponseAppendTx:
|
||||||
|
counter += 1
|
||||||
|
if res.RetCode != types.RetCodeOK {
|
||||||
|
t.Error("AppendTx failed with ret_code", res.RetCode)
|
||||||
|
}
|
||||||
|
if counter > numAppendTxs {
|
||||||
|
t.Fatal("Too many AppendTx responses")
|
||||||
|
}
|
||||||
|
t.Log("response", counter)
|
||||||
|
if counter == numAppendTxs {
|
||||||
|
go func() {
|
||||||
|
time.Sleep(time.Second * 2) // Wait for a bit to allow counter overflow
|
||||||
|
close(done)
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
case types.ResponseFlush:
|
||||||
|
// ignore
|
||||||
|
default:
|
||||||
|
t.Error("Unexpected response type", reflect.TypeOf(res))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// Write requests
|
// Write requests
|
||||||
for {
|
for counter := 0; counter < numAppendTxs; counter++ {
|
||||||
|
// Send request
|
||||||
var n int
|
var n int
|
||||||
var err error
|
var err error
|
||||||
var req types.Request = types.RequestAppendTx{TxBytes: []byte("test")}
|
var req types.Request = types.RequestAppendTx{TxBytes: []byte("test")}
|
||||||
wire.WriteBinary(req, conn, &n, &err)
|
wire.WriteBinary(req, conn, &n, &err)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Exit(err.Error())
|
t.Fatal(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sometimes send flush messages
|
||||||
|
if counter%123 == 0 {
|
||||||
|
t.Log("flush")
|
||||||
|
wire.WriteBinary(types.RequestFlush{}, conn, &n, &err)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err.Error())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// fmt.Println("Wrote", n)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Send final flush message
|
||||||
|
var n int
|
||||||
|
wire.WriteBinary(types.RequestFlush{}, conn, &n, &err)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
<-done
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ import (
|
|||||||
"github.com/tendermint/tmsp/types"
|
"github.com/tendermint/tmsp/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
var maxNumberConnections = 2
|
// var maxNumberConnections = 2
|
||||||
|
|
||||||
func StartListener(protoAddr string, app types.Application) (net.Listener, error) {
|
func StartListener(protoAddr string, app types.Application) (net.Listener, error) {
|
||||||
parts := strings.SplitN(protoAddr, "://", 2)
|
parts := strings.SplitN(protoAddr, "://", 2)
|
||||||
@ -23,33 +23,51 @@ func StartListener(protoAddr string, app types.Application) (net.Listener, error
|
|||||||
|
|
||||||
// A goroutine to accept a connection.
|
// A goroutine to accept a connection.
|
||||||
go func() {
|
go func() {
|
||||||
|
// semaphore := make(chan struct{}, maxNumberConnections)
|
||||||
semaphore := make(chan struct{}, maxNumberConnections)
|
|
||||||
|
|
||||||
for {
|
for {
|
||||||
semaphore <- struct{}{}
|
// semaphore <- struct{}{}
|
||||||
|
|
||||||
// Accept a connection
|
// Accept a connection
|
||||||
|
fmt.Println("Waiting for new connection...")
|
||||||
conn, err := ln.Accept()
|
conn, err := ln.Accept()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Exit("Failed to accept connection")
|
Exit("Failed to accept connection")
|
||||||
} else {
|
} else {
|
||||||
fmt.Println("Accepted a new connection")
|
fmt.Println("Accepted a new connection")
|
||||||
}
|
}
|
||||||
connClosed := make(chan struct{}, 2) // Push to signal connection closed
|
|
||||||
|
appContext := app.Open()
|
||||||
|
closeConn := make(chan error, 2) // Push to signal connection closed
|
||||||
responses := make(chan types.Response, 1000) // A channel to buffer responses
|
responses := make(chan types.Response, 1000) // A channel to buffer responses
|
||||||
|
|
||||||
// Read requests from conn and deal with them
|
// Read requests from conn and deal with them
|
||||||
go handleRequests(app, connClosed, conn, responses)
|
go handleRequests(appContext, closeConn, conn, responses)
|
||||||
// Pull responses from 'responses' and write them to conn.
|
// Pull responses from 'responses' and write them to conn.
|
||||||
go handleResponses(connClosed, responses, conn)
|
go handleResponses(closeConn, responses, conn)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
// Wait until connection is closed
|
// Wait until signal to close connection
|
||||||
<-connClosed
|
errClose := <-closeConn
|
||||||
fmt.Println("Connection was closed. Waiting for new connection...")
|
if errClose != nil {
|
||||||
|
fmt.Printf("Connection error: %v\n", errClose)
|
||||||
|
} else {
|
||||||
|
fmt.Println("Connection was closed.")
|
||||||
|
}
|
||||||
|
|
||||||
<-semaphore
|
// Close the connection
|
||||||
|
err := conn.Close()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error in closing connection: %v\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close the AppContext
|
||||||
|
err = appContext.Close()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error in closing app context: %v\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// <-semaphore
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,7 +77,7 @@ func StartListener(protoAddr string, app types.Application) (net.Listener, error
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Read requests from conn and deal with them
|
// Read requests from conn and deal with them
|
||||||
func handleRequests(app types.Application, connClosed chan struct{}, conn net.Conn, responses chan<- types.Response) {
|
func handleRequests(appC types.AppContext, closeConn chan error, conn net.Conn, responses chan<- types.Response) {
|
||||||
var count int
|
var count int
|
||||||
var bufReader = bufio.NewReader(conn)
|
var bufReader = bufio.NewReader(conn)
|
||||||
for {
|
for {
|
||||||
@ -68,48 +86,47 @@ func handleRequests(app types.Application, connClosed chan struct{}, conn net.Co
|
|||||||
var req types.Request
|
var req types.Request
|
||||||
wire.ReadBinaryPtr(&req, bufReader, 0, &n, &err)
|
wire.ReadBinaryPtr(&req, bufReader, 0, &n, &err)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("Error in handleRequests:", err.Error())
|
closeConn <- fmt.Errorf("Error in handleRequests: %v", err.Error())
|
||||||
connClosed <- struct{}{}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
count++
|
count++
|
||||||
handleRequest(app, req, responses)
|
handleRequest(appC, req, responses)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleRequest(app types.Application, req types.Request, responses chan<- types.Response) {
|
func handleRequest(appC types.AppContext, req types.Request, responses chan<- types.Response) {
|
||||||
switch req := req.(type) {
|
switch req := req.(type) {
|
||||||
case types.RequestEcho:
|
case types.RequestEcho:
|
||||||
msg := app.Echo(req.Message)
|
msg := appC.Echo(req.Message)
|
||||||
responses <- types.ResponseEcho{msg}
|
responses <- types.ResponseEcho{msg}
|
||||||
case types.RequestFlush:
|
case types.RequestFlush:
|
||||||
responses <- types.ResponseFlush{}
|
responses <- types.ResponseFlush{}
|
||||||
case types.RequestInfo:
|
case types.RequestInfo:
|
||||||
data := app.Info()
|
data := appC.Info()
|
||||||
responses <- types.ResponseInfo{data}
|
responses <- types.ResponseInfo{data}
|
||||||
case types.RequestSetOption:
|
case types.RequestSetOption:
|
||||||
retCode := app.SetOption(req.Key, req.Value)
|
retCode := appC.SetOption(req.Key, req.Value)
|
||||||
responses <- types.ResponseSetOption{retCode}
|
responses <- types.ResponseSetOption{retCode}
|
||||||
case types.RequestAppendTx:
|
case types.RequestAppendTx:
|
||||||
events, retCode := app.AppendTx(req.TxBytes)
|
events, retCode := appC.AppendTx(req.TxBytes)
|
||||||
responses <- types.ResponseAppendTx{retCode}
|
responses <- types.ResponseAppendTx{retCode}
|
||||||
for _, event := range events {
|
for _, event := range events {
|
||||||
responses <- types.ResponseEvent{event}
|
responses <- types.ResponseEvent{event}
|
||||||
}
|
}
|
||||||
case types.RequestGetHash:
|
case types.RequestGetHash:
|
||||||
hash, retCode := app.GetHash()
|
hash, retCode := appC.GetHash()
|
||||||
responses <- types.ResponseGetHash{retCode, hash}
|
responses <- types.ResponseGetHash{retCode, hash}
|
||||||
case types.RequestCommit:
|
case types.RequestCommit:
|
||||||
retCode := app.Commit()
|
retCode := appC.Commit()
|
||||||
responses <- types.ResponseCommit{retCode}
|
responses <- types.ResponseCommit{retCode}
|
||||||
case types.RequestRollback:
|
case types.RequestRollback:
|
||||||
retCode := app.Rollback()
|
retCode := appC.Rollback()
|
||||||
responses <- types.ResponseRollback{retCode}
|
responses <- types.ResponseRollback{retCode}
|
||||||
case types.RequestAddListener:
|
case types.RequestAddListener:
|
||||||
retCode := app.AddListener(req.EventKey)
|
retCode := appC.AddListener(req.EventKey)
|
||||||
responses <- types.ResponseAddListener{retCode}
|
responses <- types.ResponseAddListener{retCode}
|
||||||
case types.RequestRemListener:
|
case types.RequestRemListener:
|
||||||
retCode := app.RemListener(req.EventKey)
|
retCode := appC.RemListener(req.EventKey)
|
||||||
responses <- types.ResponseRemListener{retCode}
|
responses <- types.ResponseRemListener{retCode}
|
||||||
default:
|
default:
|
||||||
responses <- types.ResponseException{"Unknown request"}
|
responses <- types.ResponseException{"Unknown request"}
|
||||||
@ -117,7 +134,7 @@ func handleRequest(app types.Application, req types.Request, responses chan<- ty
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Pull responses from 'responses' and write them to conn.
|
// Pull responses from 'responses' and write them to conn.
|
||||||
func handleResponses(connClosed chan struct{}, responses <-chan types.Response, conn net.Conn) {
|
func handleResponses(closeConn chan error, responses <-chan types.Response, conn net.Conn) {
|
||||||
var count int
|
var count int
|
||||||
var bufWriter = bufio.NewWriter(conn)
|
var bufWriter = bufio.NewWriter(conn)
|
||||||
for {
|
for {
|
||||||
@ -126,15 +143,13 @@ func handleResponses(connClosed chan struct{}, responses <-chan types.Response,
|
|||||||
var err error
|
var err error
|
||||||
wire.WriteBinary(res, bufWriter, &n, &err)
|
wire.WriteBinary(res, bufWriter, &n, &err)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err.Error())
|
closeConn <- fmt.Errorf("Error in handleResponses: %v", err.Error())
|
||||||
connClosed <- struct{}{}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if _, ok := res.(types.ResponseFlush); ok {
|
if _, ok := res.(types.ResponseFlush); ok {
|
||||||
err = bufWriter.Flush()
|
err = bufWriter.Flush()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err.Error())
|
closeConn <- fmt.Errorf("Error in handleResponses: %v", err.Error())
|
||||||
connClosed <- struct{}{}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,12 @@ package types
|
|||||||
|
|
||||||
type Application interface {
|
type Application interface {
|
||||||
|
|
||||||
|
// For new socket connections
|
||||||
|
Open() AppContext
|
||||||
|
}
|
||||||
|
|
||||||
|
type AppContext interface {
|
||||||
|
|
||||||
// Echo a message
|
// Echo a message
|
||||||
Echo(message string) string
|
Echo(message string) string
|
||||||
|
|
||||||
@ -28,4 +34,7 @@ type Application interface {
|
|||||||
|
|
||||||
// Remove event listener
|
// Remove event listener
|
||||||
RemListener(key string) RetCode
|
RemListener(key string) RetCode
|
||||||
|
|
||||||
|
// Close this AppContext
|
||||||
|
Close() error
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user