-
-
Notifications
You must be signed in to change notification settings - Fork 116
api: cffi api to create account manager with existing events channel to see events emitted during startup. dc_event_channel_new, dc_event_channel_unref, dc_event_channel_get_event_emitter and dc_accounts_new_with_event_channel
#7609
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -22,6 +22,7 @@ typedef struct _dc_lot dc_lot_t; | |||||
| typedef struct _dc_provider dc_provider_t; | ||||||
| typedef struct _dc_event dc_event_t; | ||||||
| typedef struct _dc_event_emitter dc_event_emitter_t; | ||||||
| typedef struct _dc_event_channel dc_event_channel_t; | ||||||
| typedef struct _dc_jsonrpc_instance dc_jsonrpc_instance_t; | ||||||
| typedef struct _dc_backup_provider dc_backup_provider_t; | ||||||
|
|
||||||
|
|
@@ -3114,6 +3115,35 @@ int dc_receive_backup (dc_context_t* context, const char* qr); | |||||
| */ | ||||||
| dc_accounts_t* dc_accounts_new (const char* dir, int writable); | ||||||
|
|
||||||
| /** | ||||||
| * Create a new account manager with an existing events channel, | ||||||
| * which allows you see events emitted during startup. | ||||||
| * | ||||||
| * The account manager takes an directory | ||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Maybe it's better not to duplicate the documentation, but just add a reference to |
||||||
| * where all context-databases are placed in. | ||||||
| * To add a context to the account manager, | ||||||
| * use dc_accounts_add_account() or dc_accounts_migrate_account(). | ||||||
| * All account information are persisted. | ||||||
| * To remove a context from the account manager, | ||||||
| * use dc_accounts_remove_account(). | ||||||
| * | ||||||
| * @memberof dc_accounts_t | ||||||
| * @param dir The directory to create the context-databases in. | ||||||
| * If the directory does not exist, | ||||||
| * dc_accounts_new_with_event_channel() will try to create it. | ||||||
| * @param writable Whether the returned account manager is writable, i.e. calling these functions on | ||||||
| * it is possible: dc_accounts_add_account(), dc_accounts_add_closed_account(), | ||||||
| * dc_accounts_migrate_account(), dc_accounts_remove_account(), dc_accounts_select_account(). | ||||||
| * @param dc_event_channel_t Events Channel to be used for this accounts manager, | ||||||
| * create one with dc_event_channel_new(). | ||||||
| * This channel is consumed by this method and can not be used again afterwards, | ||||||
| * so be sure to call `dc_event_channel_get_event_emitter` before. | ||||||
| * @return An account manager object. | ||||||
| * The object must be passed to the other account manager functions | ||||||
| * and must be freed using dc_accounts_unref() after usage. | ||||||
| * On errors, NULL is returned. | ||||||
| */ | ||||||
| dc_accounts_t* dc_accounts_new_with_event_channel(const char* dir, int writable, dc_event_channel_t* events_channel); | ||||||
|
|
||||||
| /** | ||||||
| * Free an account manager object. | ||||||
|
|
@@ -5999,6 +6029,58 @@ char* dc_jsonrpc_next_response(dc_jsonrpc_instance_t* jsonrpc_instance); | |||||
| */ | ||||||
| char* dc_jsonrpc_blocking_call(dc_jsonrpc_instance_t* jsonrpc_instance, const char *input); | ||||||
|
|
||||||
| /** | ||||||
| * @class dc_event_channel_t | ||||||
| * | ||||||
| * Opaque object that is used to create an event emitter which can be used log events during startup of an accounts manger. | ||||||
| * Only used for dc_accounts_new_with_event_channel(). | ||||||
| * To use it: | ||||||
| * 1. create an events channel with `dc_event_channel_new()`. | ||||||
| * 2. get an event emitter for it with `dc_event_channel_get_event_emitter()`. | ||||||
| * 3. use it to create your account manager with `dc_accounts_new_with_event_channel()`, which consumes the channel. | ||||||
| * 4. free the empty channel wrapper object with `dc_event_channel_unref()`. | ||||||
| */ | ||||||
|
|
||||||
| /** | ||||||
| * Create a new event channel. | ||||||
| * | ||||||
| * @memberof dc_event_channel_t | ||||||
| * @return An event channel wrapper object (dc_event_channel_t). | ||||||
| */ | ||||||
| dc_event_channel_t* dc_event_channel_new(); | ||||||
|
|
||||||
| /** | ||||||
| * Release/free the events channel structure. | ||||||
| * This function releases the memory of the `dc_event_channel_t` structure. | ||||||
| * | ||||||
| * you can call it after calling dc_accounts_new_with_event_channel, | ||||||
| * which took the events channel out of it already, so this just frees the underlying option. | ||||||
| * | ||||||
| * @memberof dc_event_channel_t | ||||||
| */ | ||||||
| void dc_event_channel_unref(dc_event_channel_t* event_channel); | ||||||
|
|
||||||
| /** | ||||||
| * Create the event emitter that is used to receive events. | ||||||
| * | ||||||
| * The library will emit various @ref DC_EVENT events, such as "new message", "message read" etc. | ||||||
| * To get these events, you have to create an event emitter using this function | ||||||
| * and call dc_get_next_event() on the emitter. | ||||||
| * | ||||||
| * This is similar to dc_get_event_emitter(), which, however, | ||||||
| * must not be called for accounts handled by the account manager. | ||||||
| * | ||||||
| * @memberof dc_event_channel_t | ||||||
| * @param The event channel. | ||||||
| * @return Returns the event emitter, NULL on errors. | ||||||
| * Must be freed using dc_event_emitter_unref() after usage. | ||||||
| * | ||||||
| * Note: Use only one event emitter per account manager. | ||||||
| * Having more than one event emitter running at the same time on the same account manager | ||||||
| * will result in events randomly delivered to the one or to the other. | ||||||
|
Comment on lines
+6078
to
+6080
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this still the case?, rust code looks like it allows multiple listeners: it uses and async broadcast.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looks like it, probably it was forgotten in #5478 to update the documentation. But of course, the only way to be totally sure is actually trying it out. |
||||||
| */ | ||||||
| dc_event_emitter_t* dc_event_channel_get_event_emitter(dc_event_channel_t* event_channel); | ||||||
|
|
||||||
| /** | ||||||
| * @class dc_event_emitter_t | ||||||
| * | ||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -11,6 +11,7 @@ | |||||||||||
| #[macro_use] | ||||||||||||
| extern crate human_panic; | ||||||||||||
|
|
||||||||||||
| use std::cell::Cell; | ||||||||||||
| use std::collections::BTreeMap; | ||||||||||||
| use std::convert::TryFrom; | ||||||||||||
| use std::fmt::Write; | ||||||||||||
|
|
@@ -4785,6 +4786,91 @@ pub unsafe extern "C" fn dc_accounts_new( | |||||||||||
| } | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| pub type dc_event_channel_t = Cell<Option<Events>>; | ||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I.e. Mutex instead of Cell and Arc instead of Box below. OTOH do we need thread safety here? We can just document that it's not thread-safe. By using
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
All CFFI API is declared as thread-safe so we don't have to think about which is safe and which is not: core/deltachat-ffi/deltachat.h Lines 143 to 147 in fd90493
It's probably not really true that all code is thread-safe because we don't have Mutexes around |
||||||||||||
|
|
||||||||||||
| #[no_mangle] | ||||||||||||
| pub unsafe extern "C" fn dc_event_channel_new() -> *mut dc_event_channel_t { | ||||||||||||
| Box::into_raw(Box::new(Cell::new(Some(Events::new())))) | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| /// Release the events channel structure. | ||||||||||||
| /// | ||||||||||||
| /// This function releases the memory of the `dc_event_channel_t` structure. | ||||||||||||
| /// | ||||||||||||
| /// you can call it after calling dc_accounts_new_with_event_channel, | ||||||||||||
| /// which took the events channel out of it already, so this just frees the underlying option. | ||||||||||||
| #[no_mangle] | ||||||||||||
| pub unsafe extern "C" fn dc_event_channel_unref(event_channel: *mut dc_event_channel_t) { | ||||||||||||
| if event_channel.is_null() { | ||||||||||||
| eprintln!("ignoring careless call to dc_event_channel_unref()"); | ||||||||||||
| return; | ||||||||||||
| } | ||||||||||||
| let _ = Box::from_raw(event_channel); | ||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We write this as
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yeah that's clearer, though "everywhere" is not quite correct, at-least not from the place I copied this from. could be a follow up pr to make sure it is the same everywhere, though I'm not sure how important it is for the existing calls. |
||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| #[no_mangle] | ||||||||||||
| pub unsafe extern "C" fn dc_event_channel_get_event_emitter( | ||||||||||||
| event_channel: *mut dc_event_channel_t, | ||||||||||||
| ) -> *mut dc_event_emitter_t { | ||||||||||||
| if event_channel.is_null() { | ||||||||||||
| eprintln!("ignoring careless call to dc_event_channel_get_event_emitter()"); | ||||||||||||
| return ptr::null_mut(); | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| let Some(event_channel) = &*(*event_channel).as_ptr() else { | ||||||||||||
| eprintln!( | ||||||||||||
| "ignoring careless call to dc_event_channel_get_event_emitter() | ||||||||||||
| -> channel was already consumed, make sure you call this before dc_accounts_new_with_event_channel" | ||||||||||||
| ); | ||||||||||||
| return ptr::null_mut(); | ||||||||||||
| }; | ||||||||||||
|
|
||||||||||||
| let emitter = event_channel.get_emitter(); | ||||||||||||
|
|
||||||||||||
| Box::into_raw(Box::new(emitter)) | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| #[no_mangle] | ||||||||||||
| pub unsafe extern "C" fn dc_accounts_new_with_event_channel( | ||||||||||||
| dir: *const libc::c_char, | ||||||||||||
| writable: libc::c_int, | ||||||||||||
| event_channel: *mut dc_event_channel_t, | ||||||||||||
Simon-Laux marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||
| ) -> *mut dc_accounts_t { | ||||||||||||
| setup_panic!(); | ||||||||||||
|
|
||||||||||||
| if dir.is_null() || event_channel.is_null() { | ||||||||||||
| eprintln!("ignoring careless call to dc_accounts_new_with_event_channel()"); | ||||||||||||
| return ptr::null_mut(); | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| // consuming channel enforce that you need to get the event emitter | ||||||||||||
| // before initializing the account manager, | ||||||||||||
| // so that you don't miss events/errors during initialisation. | ||||||||||||
| // It also prevents you from using the same channel on multiple account managers. | ||||||||||||
| let Some(event_channel) = (*event_channel).take() else { | ||||||||||||
| eprintln!( | ||||||||||||
| "ignoring careless call to dc_accounts_new_with_event_channel() | ||||||||||||
| -> channel was already consumed" | ||||||||||||
| ); | ||||||||||||
| return ptr::null_mut(); | ||||||||||||
| }; | ||||||||||||
|
|
||||||||||||
| let accs = block_on(Accounts::new_with_events( | ||||||||||||
| as_path(dir).into(), | ||||||||||||
| writable != 0, | ||||||||||||
| event_channel, | ||||||||||||
| )); | ||||||||||||
|
|
||||||||||||
| match accs { | ||||||||||||
| Ok(accs) => Box::into_raw(Box::new(AccountsWrapper::new(accs))), | ||||||||||||
| Err(err) => { | ||||||||||||
| // We are using Anyhow's .context() and to show the inner error, too, we need the {:#}: | ||||||||||||
| eprintln!("failed to create accounts: {err:#}"); | ||||||||||||
| ptr::null_mut() | ||||||||||||
| } | ||||||||||||
| } | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| /// Release the accounts structure. | ||||||||||||
| /// | ||||||||||||
| /// This function releases the memory of the `dc_accounts_t` structure. | ||||||||||||
|
|
||||||||||||
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.