-
Notifications
You must be signed in to change notification settings - Fork 51
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(libooniengine): add support for running tasks #1112
base: master
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should temporarily stash part of the code we have in this pull request and just focus on adding support for running tasks, without additional complexity that may distract us.
I think the right next-step is to just flesh out how the Dart consumer will interact with tasks.
internal/engine/session.go
Outdated
@@ -221,6 +221,63 @@ func NewSession(ctx context.Context, config SessionConfig) (*Session, error) { | |||
return sess, nil | |||
} | |||
|
|||
func NewSessionWithoutTunnel(ctx context.Context, config *SessionConfig) (*Session, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not convinced we need this new constructor
internal/engine/session.go
Outdated
return nil, errors.New("SoftwareVersion is empty") | ||
} | ||
if config.KVStore == nil { | ||
config.KVStore = &kvstore.Memory{} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should not mutate a config passed by pointer.
internal/libooniengine/engine.h
Outdated
|
||
// OONITask |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// OONITask |
internal/libooniengine/engine.h
Outdated
/// | ||
/// @return Zero on failure, nonzero on success. If the return value | ||
/// is nonzero, a task is running. In such a case, the caller is | ||
/// responsible to eventually dispose of the task using OONIEngineFreeMemory. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you sure we can use OONIEngineFreeMemory
to dispose of a task? I thought we should be having a dedicated API that removes the corresponding handle from the corresponding map.
internal/libooniengine/engine.h
Outdated
/// | ||
/// @return A NULL pointer on failure, non-NULL otherwise. If the return | ||
/// value is non-NULL, the caller takes ownership of the OONIMessage | ||
/// pointer and MUST free it using OONIMessageFree when done using it. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this say OONIEngineFreeMemory
?
internal/libooniengine/logger.go
Outdated
"github.com/ooni/probe-cli/v3/internal/model" | ||
) | ||
|
||
type LogLevel int32 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cannot these be strings? I think it's easier for a consumer if these are strings.
internal/libooniengine/logger.go
Outdated
func newTaskLogger(emitter taskMaybeEmitter) *taskLogger { | ||
return &taskLogger{ | ||
emitter: emitter, | ||
verbose: false, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The constructor should also initialize verbose
.
internal/libooniengine/main.go
Outdated
err := json.Unmarshal([]byte(C.GoString(req)), out) | ||
if err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
err := json.Unmarshal([]byte(C.GoString(req)), out) | |
if err != nil { | |
if err := json.Unmarshal([]byte(C.GoString(req)), out); err != nil { |
internal/libooniengine/model.go
Outdated
waitForNextEvent(timeout time.Duration) *response | ||
|
||
// isDone implements OONITaskIsDone. | ||
isDone() bool |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where are you implementing isDone
?
internal/libooniengine/ticker.go
Outdated
|
||
// runTicker emits a ticker event every second. It is a subtask | ||
// associated with all tasks. | ||
func runTicker(ctx context.Context, close chan any, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because we have a timeout associated with each WaitForNextEvent, we don't need a ticker.
Thank you for the feedback! I have made the requested changes. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did another pass and provided lots of suggestions!
Thanks for moving this forward! 🙌
internal/libooniengine/engine.h
Outdated
/// engine that performs a background operation and emits meaningful | ||
/// events such as, for example, the results of measurements. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This description should be updated to mention that we emit interim outputs such as logs and progress as well as the result of the operation.
internal/libooniengine/engine.h
Outdated
void OONIENgineFreeMemory(void *ptr); | ||
void OONIEngineFreeMemory(void *ptr); | ||
|
||
/// OONIEngineCall starts a new OONITask using the given [req]. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When I wrote the prototype, I used [req]
but it seems the right syntax when using Doxygen is @p req
. Do you mind updating the documentation in this file?
internal/libooniengine/engine.h
Outdated
/// value is non-NULL, the caller takes ownership of the char pointer | ||
/// and MUST free it using OONIEngineFreeMemory when done using it. | ||
/// | ||
/// This function will return an empty string: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am confused by this documentation. It strikes me as bad to return NULL or an empty string or a valid string and it would be better for the return value to be either NULL or a valid JSON string. I think we should double check the implementation to be sure it actually does what is documented or whether the documentation was just inconsistent with the actual implementation. (TODO(@bassosimone))
internal/libooniengine/engine.h
Outdated
/// value is non-NULL, the caller takes ownership of the char pointer | ||
/// and MUST free it using OONIEngineFreeMemory when done using it. | ||
/// | ||
/// This function will return an empty string: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we need a timeout for this function call. It should be called after the task is done, and there should be a result once this happens. So, we can simplify the life of the caller by removing support for the timeout.
internal/libooniengine/engine.h
Outdated
/// checking whether this function has returned an empty string. | ||
char *OONIEngineTaskGetResult(OONITask task, int32_t timeout); | ||
|
||
/// OONIEngineTaskIsDone returns whether the task identified by [taskID] is done. A taks is |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/// OONIEngineTaskIsDone returns whether the task identified by [taskID] is done. A taks is | |
/// OONIEngineTaskIsDone returns whether the task identified by [taskID] is done. A task is |
internal/motor/task.go
Outdated
r := reflect.ValueOf(req) | ||
t := r.Type() | ||
for i := 0; i < r.NumField(); i++ { | ||
if !r.Field(i).IsNil() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IIUC, the Request
contains structs and not pointers. Because of this, the check that would actually work here would be IsZero
. In any case, I suggested pointers, so IsNil
would be okay.
That said, I am not sure we should implement task dispatching using reflection like this.
internal/motor/task.go
Outdated
result: make(chan *Response, 1), | ||
stopped: make(chan any), | ||
} | ||
go tp.main(ctx, name, req) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we're dispatching by name, we should probably add a Name
to the Request
struct. If we want to dispatch by pointer, otherwise, we can avoid passing name
field here and only check for the req
fields.
internal/motor/task.go
Outdated
return nil // timeout while blocking for read | ||
case ev := <-tp.result: | ||
return ev // block for read till we receive a result | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
as I mentioned when discussing the C API, this can just be return <-tp.result
internal/motor/task.go
Outdated
for !tp.IsDone() { | ||
const blockForever = -1 | ||
_ = tp.WaitForNextEvent(blockForever) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am wondering if draining the channel is actually needed. IIUC, we always emit in nonblocking mode.
runner := taskRegistry[name] | ||
if runner == nil { | ||
log.Printf("OONITaskStart: unknown task name: %s", name) | ||
return |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We cannot just return here. We now always need to tp.result <- resp
.
Checklist
Description
This diff adds support for running tasks in
libooniengine
which are subject to interaction via the FFI consumer. (edited)