-
Notifications
You must be signed in to change notification settings - Fork 15
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
mobile: Go based replacement for MKAsyncTask (#347)
See #347
- Loading branch information
1 parent
3fc6d3e
commit 357cb1c
Showing
14 changed files
with
1,446 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
# OONI Measurement Engine | ||
|
||
| Author | Simone Basso | | ||
|--------------|--------------| | ||
| Last-Updated | 2020-02-18 | | ||
| Status | under review | | ||
|
||
## Introduction | ||
|
||
We want to write experiments in Go. This reduces our burden | ||
compared to writing them using C/C++ code. | ||
|
||
Go consumers of probe-engine shall directly use its Go API. We | ||
will discuss the Go API in a future revision of this spec. | ||
|
||
For mobile apps, we want to replace these MK APIs: | ||
|
||
- [measurement-kit/android-libs](https://github.com/measurement-kit/android-libs) | ||
|
||
- [measurement-kit/mkall-ios](https://github.com/measurement-kit/mkall-ios) | ||
|
||
We also want consumers of [measurement-kit's FFI API](https://git.io/Jv4Rv) | ||
to be able to replace measurement-kit with probe-engine. | ||
|
||
## APIs to replace | ||
|
||
### Mobile APIs | ||
|
||
We define a Go API that `gomobile` binds to a Java/ObjectiveC | ||
API that is close enough to the MK's mobile APIs. | ||
|
||
### FFI API | ||
|
||
We define a CGO API such that `go build -buildmode=c-shared` | ||
yields an API reasonably close to MK's FFI API. | ||
|
||
## Running experiments | ||
|
||
It seems the generic API for enabling running experiments both on | ||
mobile devices and for FFI consumers is like: | ||
|
||
```Go | ||
type Task struct{ ... } | ||
func StartTask(input string) (*Task, error) | ||
func (t *Task) Interrupt() | ||
func (t *Task) IsDone() bool | ||
func (t *Task) WaitForNextEvent() string | ||
``` | ||
|
||
This should be enough to generate a suitable mobile API when | ||
using the `gomobile` Go subcommand. | ||
|
||
We can likewise generate a FFI API as follows: | ||
|
||
```Go | ||
package main | ||
|
||
import ( | ||
"C" | ||
"sync" | ||
|
||
"github.com/ooni/probe-engine/oonimkall" | ||
) | ||
|
||
var ( | ||
idx int64 = 1 | ||
m = make(map[int64]*oonimkall.Task) | ||
mu sync.Mutex | ||
) | ||
|
||
//export ooni_task_start | ||
func ooni_task_start(settings string) int64 { | ||
tp, err := oonimkall.StartTask(settings) | ||
if err != nil { | ||
return 0 | ||
} | ||
mu.Lock() | ||
handle := idx | ||
idx++ | ||
m[handle] = tp | ||
mu.Unlock() | ||
return handle | ||
} | ||
|
||
//export ooni_task_interrupt | ||
func ooni_task_interrupt(handle int64) { | ||
mu.Lock() | ||
if tp := m[handle]; tp != nil { | ||
tp.Interrupt() | ||
} | ||
mu.Unlock() | ||
} | ||
|
||
//export ooni_task_is_done | ||
func ooni_task_is_done(handle int64) bool { | ||
isdone := true | ||
mu.Lock() | ||
if tp := m[handle]; tp != nil { | ||
isdone = tp.IsDone() | ||
} | ||
mu.Unlock() | ||
return isdone | ||
} | ||
|
||
//export ooni_task_wait_for_next_event | ||
func ooni_task_wait_for_next_event(handle int64) (event string) { | ||
mu.Lock() | ||
tp := m[handle] | ||
mu.Unlock() | ||
if tp != nil { | ||
event = tp.WaitForNextEvent() | ||
} | ||
return | ||
} | ||
|
||
func main() {} | ||
``` | ||
|
||
This is close enough to [measurement-kit's FFI API](https://git.io/Jv4Rv) that | ||
a few lines of C allow to implement an ABI compatible replacement. | ||
|
||
## Other APIs of interest | ||
|
||
We currently don't have plans for replacing other APIs. | ||
|
||
## History | ||
|
||
[The initial version of this design document]( | ||
https://github.com/measurement-kit/engine/blob/master/DESIGN.md) | ||
lived in the measurement-kit namespace at GitHub. It discussed | ||
a bunch of broad, extra topics, e.g., code bloat that are not | ||
discussed in this document. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
package oonimkall | ||
|
||
import ( | ||
"fmt" | ||
) | ||
|
||
// chanLogger is a logger targeting a channel | ||
type chanLogger struct { | ||
emitter *eventEmitter | ||
hasdebug bool | ||
hasinfo bool | ||
haswarning bool | ||
out chan<- *eventRecord | ||
settings *settingsRecord | ||
} | ||
|
||
// Debug implements Logger.Debug | ||
func (cl *chanLogger) Debug(msg string) { | ||
if cl.hasdebug { | ||
cl.emitter.Emit("log", eventValue{ | ||
LogLevel: "DEBUG", | ||
Message: msg, | ||
}) | ||
} | ||
} | ||
|
||
// Debugf implements Logger.Debugf | ||
func (cl *chanLogger) Debugf(format string, v ...interface{}) { | ||
if cl.hasdebug { | ||
cl.Debug(fmt.Sprintf(format, v...)) | ||
} | ||
} | ||
|
||
// Info implements Logger.Info | ||
func (cl *chanLogger) Info(msg string) { | ||
if cl.hasinfo { | ||
cl.emitter.Emit("log", eventValue{ | ||
LogLevel: "INFO", | ||
Message: msg, | ||
}) | ||
} | ||
} | ||
|
||
// Infof implements Logger.Infof | ||
func (cl *chanLogger) Infof(format string, v ...interface{}) { | ||
if cl.hasinfo { | ||
cl.Info(fmt.Sprintf(format, v...)) | ||
} | ||
} | ||
|
||
// Warn implements Logger.Warn | ||
func (cl *chanLogger) Warn(msg string) { | ||
if cl.haswarning { | ||
cl.emitter.Emit("log", eventValue{ | ||
LogLevel: "WARNING", | ||
Message: msg, | ||
}) | ||
} | ||
} | ||
|
||
// Warnf implements Logger.Warnf | ||
func (cl *chanLogger) Warnf(format string, v ...interface{}) { | ||
if cl.haswarning { | ||
cl.Warn(fmt.Sprintf(format, v...)) | ||
} | ||
} | ||
|
||
// newChanLogger creates a new ChanLogger instance. | ||
func newChanLogger( | ||
emitter *eventEmitter, settings *settingsRecord, | ||
out chan<- *eventRecord, | ||
) *chanLogger { | ||
cl := &chanLogger{ | ||
emitter: emitter, | ||
out: out, | ||
settings: settings, | ||
} | ||
switch settings.LogLevel { | ||
case "DEBUG", "DEBUG2": | ||
cl.hasdebug = true | ||
fallthrough | ||
case "INFO": | ||
cl.hasinfo = true | ||
fallthrough | ||
case "ERR", "WARNING": | ||
fallthrough | ||
default: | ||
cl.haswarning = true | ||
} | ||
return cl | ||
} |
Oops, something went wrong.