How to establish external timeout for notify-rust - rust

I'm trying to use notify-rust library on Ubuntu 20.04, but the notifications that are emitted have unpredictable timeouts. Also, if I send several notifications with short delays between them, the results may vary (sometimes I see all notifications, sometimes only several of them).
The workarounds I applied:
Critical Urgency, so the notifications would stay displayed forever.
Wait for an action before displaying the next notification, so no notifications would be lost.
// add `notify-rust = "4"` to dependencies for this code to work
use notify_rust::{Notification, Urgency};
use std::{thread, time};
fn main() {
let summaries = ["one", "two"];
for summary in summaries {
let handle = Notification::new()
.summary(summary)
.urgency(Urgency::Critical)
.show()
.unwrap();
// helps displaying all the notifications
handle.wait_for_action(|_action| ());
// delay might vary
thread::sleep(time::Duration::from_millis(10));
}
}
I also experimented with the lib's timeout, but didn't get any good results.
The primary issue with the wait_for_action approach, is that sometimes no notification is seen on the screen after it was sent, so the main thread is waiting for an action on that notification that would never happen (nothing to click on).
I want to try a watchdog approach: start a thread, send the notification handler into it, and close the notification after some timeout:
let handle = Notification::new()
...
let thread_handle = handle.clone();
thread::spawn(move || {
thread::sleep(time::Duration::from_secs(60));
thread_handle.close();
});
handle.wait_for_action(|_action| ());
I'm not sure how to do that part. I can't use handle both for the thread and for wait_for_action, because the thread takes the ownership. Cloning doesn't work properly, instead of NotificationHandle it produces Notification.
Is there any way to solve this without dip dig into notify-rust library internals?
I think I can resolve this using the whole different approach that won't involve calling blocking wait_for_action, but I wonder if there is any more elegant solution?

I came up with another algorithm for notifications and timeouts: instead of relying on wait_for_action being able to catch an event of a notification being closed, I created two threads:
one with the same wait_for_action that on completion sends a message through a channel
another thread that waits for some time and then sends the same message through the same channel
The main thread waits for the message and continues as the first message arrives.
use notify_rust::{Notification, Urgency};
use std::sync::mpsc;
use std::{thread, time};
fn main() {
let summaries = ["one", "two"];
for summary in summaries {
// establish the channel and a second transmitter for `timeout` thread
let (handler_tx, rx) = mpsc::channel();
let timer_tx = handler_tx.clone();
// no changes here
let handler = Notification::new()
.summary(summary)
.urgency(Urgency::Critical)
.show()
.unwrap();
// `wait_for_action` is wrapped into a thread
thread::spawn(move ||
handler.wait_for_action(|_action|
handler_tx.send(()).unwrap_or(())
)
);
// another thread that waits and then sends the same message
thread::spawn(move || {
thread::sleep(time::Duration::from_secs(7));
timer_tx.send(()).unwrap_or(());
});
// the main thread that waits for a message from any of senders
rx.recv().unwrap();
thread::sleep(time::Duration::from_millis(10));
}
}
Results of message sending are ignored on purpose: only one of those messages could be sent successfully, then the channel goes out of scope and another sender gets an error, and that's fine.
The solution has a disadvantage: it's possible that after the timeout, another notification would be sent without waiting for the previous one to be closed. I saw those notifications still can be displayed after the first one be closed even if the main thread finished, so they were waiting on the notification bus.
I also noted that there is another method implemented for NotificationHandler that probably suits better this task: on_close, but it is implemented using wait_for_action, so it's also blocking, and I can't implement notification close after timeout using this method.

Related

How can I get who paused the video in Youtube API? (with Socket.io)

Basically, I'm challenging myself to build something similar to watch2gether, where you can watch youtube videos simultaneously through the Youtube API and Socket.io.
My problem is that there's no way to check if the video has been paused other than utilizing the 'onStateChange' event of the Youtube API.
But since I cannot listen to the CLICK itself rather than the actual pause EVENT, when I emit a pause command and broadcast it via socket, when the player pauses in other sockets, it will fire the event again, and thus I'm not able to track who clicked pause first NOR prevent the pauses from looping.
This is what I currently have:
// CLIENT SIDE
// onStateChange event
function YtStateChange(event) {
if(event.data == YT.PlayerState.PAUSED) {
socket.emit('pausevideo', $user); // I'm passing the current user for future implementations
}
// (...) other states
}
// SERVER SIDE
socket.on('pausevideo', user => {
io.emit('smsg', `${user} paused the video`)
socket.broadcast.emit('pausevideo'); // Here I'm using broadcast to send the pause to all sockets beside the one who first clicked pause, since it already paused from interacting with the iframe
});
// CLIENT SIDE
socket.on('pausevideo', () => {
ytplayer.pauseVideo(); // The problem here is, once it pauses the video, onStateChange obviously fires again and results in an infinite ammount of pauses (as long as theres more than one user in the room)
});
The only possible solution I've thought of is to use a different PLAY/PAUSE button other than the actual Youtube player on the iframe to catch the click events and from there pause the player, but I know countless websites that uses the plain iframe and catch these kind of events, but I couldn't find a way to do it with my current knowledge.
If the goal here is to be able to ignore a YT.PlayerState.PAUSED event when it is specifically caused by you earlier calling ytplayer.pauseVideo(), then you can do that by recording a timestamp when you call ytplayer.pauseVideo() and then checking that timestamp when you get a YT.PlayerState.PAUSED event to see if that paused event was occurring because you just called ytplayer.pauseVideo().
The general concept is like this:
let pauseTime = 0;
const kPauseIgnoreTime = 250; // experiment with what this value should be
// CLIENT SIDE
// onStateChange event
function YtStateChange(event) {
if(event.data == YT.PlayerState.PAUSED) {
// only send pausevideo message if this pause wasn't caused by
// our own call to .pauseVideo()
if (Date.now() - pauseTime > kPauseIgnoreTime) {
socket.emit('pausevideo', $user); // I'm passing the current user for future implementations
}
}
// (...) other states
}
// CLIENT SIDE
socket.on('pausevideo', () => {
pauseTime = Date.now();
ytplayer.pauseVideo();
});
If you have more than one of these in your page, then (rather than a variable like this) you can store the pauseTime on a relevant DOM element related to which player the event is associated with.
You can do some experimentation to see what value is best for kPauseIgnoreTime. It needs to be large enough so that any YT.PlayerState.PAUSED event cause by you specifically calling ytplayer.pauseVideo() is detected, but not so long that it catches a case where someone might be pausing, then unpausing relatively soon after.
I actually found a solution while working around what that guy answered, I'm gonna be posting it in here in case anyone gets stuck with the same problem and ends up here.
Since socket.broadcast.emit doesn't emit to itself, I created a bool ignorePause and made it to be true only when the client received the pause request.
Then I only emit the socket if the pause request wasn't already broadcasted and thus received, and if so, the emit is ignored and the bool is set to false again in case this client/socket pauses the video afterwards.

Making channels constructioned in a function last the lifetime of the program in Rust

I'm trying to write a basic multithreaded application using gtk3-rs, where the main thread sends a message to a child thread when a button is clicked, and the child thread sends a message back in response, after doing some calculations, the results of which are displayed by the main thread in a dialog box.
This seems simple enough conceptually, but I'm running into a problem where the channels that I'm creating in the callback that is used by gtk::Application::connect_activate to build the user interface are getting closed before the child thread (also created in that callback, and then detached) can even use them once, let alone how I intended, which is continually throughout the life of the application.
These are glib channels on the MainContext, not MSPC channels, so instead of busy-waiting for input like for normal channels, I was able to attach a listener on both receivers. I have one listening in the main thread (attached in the UI builder callback) and one listening in the spawned thread, but apparently that's not enough to keep the channels alive, because when I try to send a message to the thread's channel, it errors out saying that the thread is closed.
So the basic structure of my code is like this:
fn connect_events(/* event box, channel a */) {
event_box.connect_button_release_event(move |_, _| {
a.send("foo").unwrap();
});
}
fn build_ui(app: &gtk::Application) {
let (a, b) = glib::MainContext::channel(glib::PRIORITY_DEFAULT);
let (c, d) = glib::MainContext::channel(glib::PRIORITY_DEFAULT);
let event_box = /* GTK event box to capture events */;
connect_events(&event_box, a.clone());
thread::spawn(move || {
b.attach(/* handle receiving a message from the main thread by sending a message back on c */);
});
d.attach(/* pop up a dialog box with whatever was sent back */);
}
fn main() {
let application = gtk::Application::new(
Some("com.example.aaaaaaaa"),
Default::default(),
);
application.connect_activate(build_ui);
application.run();
}
So, how do I convince Rust to keep the channels alive? I tried doing some lazy_static magic and using .leak(), but neither of those seemed to work, and moving all of this code out of the UI builder is unfortunately not an option.
My pragmatic answer is: Don't use glib channels.
I'm using async rust channels for things like this. In your case, a oneshot channel could be useful. But many crates provide async channels, async-std or tokio for example.
You can spawn a function via glib::MainContext::default().spawn_local() that .awaits the message(s) from the channel and show the dialog there.

Tokio thread is not starting / spawning

I'm trying to start a new task to read from a socket client. I'm using the following same method on both the websocket server and client to receive from the connection.
The problem is, on the server side, the thread is started (2 log lines printed), but on the client side the thread is not starting (only the first line printed).
If I await on the spawn(), I can receive from the client. But then the parent task cannot proceed.
Any pointers for solving this problem?
pub async fn receive_message_from_peer(
mut receiver: PeerReceiver,
sender: Sender<IoEvent>,
peer_index: u64,
) {
debug!("starting new task for reading from peer : {:?}", peer_index);
tokio::task::spawn(async move {
debug!("new thread started for peer receiving");
// ....
}); // not awaiting or join!()
Do you use tokio::TcpListener::from_std(...) to create a listener object this way?
I had the same problem as you, my std_listener object was created based on net2. So there is a scheduling incompatibility problem.
From the description in the newer official documentation https://docs.rs/tokio/latest/tokio/net/struct.TcpListener.html#method.from_std, it seems that tokio currently has better support for socket2.
So I think the issue was I was using std::thread::sleep() in async code in some places. after using tokio::time::sleep() didn't need to yield the thread.

Sequentially execute webhooks received in node application

I have a node application using koa. It receiving webhooks from external application on specific resources.
To illustrate let say the webhook send me with POST request an object of this type :
{
'resource_id':'<SomeID>',
'resource_origin':'<SomeResourceOrigin>',
'value' : '<SomeValue>'
}
I would like to execute sequentially any resources coming from the same origin to avoid desynchronization of resources related to my execution.
I was thinking to use database as lock and use cron to sequentially executing my process for each resources of same origin.
But I'm not sure it's the most efficient method.
So my question is here :
Do you know some method/package/service allowing me to use global queues that I could implement for each origin insuring resources from same origin will be executed synchronously without making all webhooks processed sequentially ? If it do not use database it's better.
If I were you I would start by serializing the handling of all your webhooks. In other words, I suggest you handle them one at a time no matter their origin. Use a simple queue inside your nodejs application.
(Once you've convinced yourself that works correctly, you can then serialize them based on origin.)
First, structure your function (let's call it handleOneWebhook()) for handling incoming webhooks as a Promise or an async function. Then you could invoke them using code with this outline.
let busy= false
async function handleManyWebhooks (queue) {
if (busy) return
busy = true
while (queue.length > 0) {
const item = queue.shift()
await handleOneWebhook (item)
}
busy = false
}
The queue you pass to handleManyWebhooks is a simple array, where each element is the object from a POST request. You use it as a queue: push() each object to put it into the queue, and shift() to remove it.
Then, whenever you receive a webhook POST object you use code with this outline.
const queue = []
...
function handlePostObject (postObject) {
queue.push(postObject)
handleManyWebooks (queue)
}
Even though you call handleManyWebhooks once for each incoming object, the busy flag makes sure it handles only one at a time.
Notice this is a very simple solution. Once you have it working correctly, two possible refinements suggest themselves.
Use something more efficient for your queue than a simple array. shift() is not very fast.
Create a separate queue object with its own busy flag for each separate origin. Then you will be able to parallelize the handling of webhooks from different origins while still serializing the stream of webhooks from each origin.
Solution I decide to use
Small brief of the post discussion
As Ivan Rubinson let me know my problem is just a producer-consumer problem.
So I finally chose to use RabbitMQ because I have a huge amount of webhook to process. For peoples having a small amount of request to process and do not want use external tools O. Jones answer is a real good way to solve the problem.
Solution design
I finally install and configure a RabbitMQ server, then I created for each origin of my web-hooks one queue.
Producer
On the producer side when I receive the web-hook data I send a message to the queue corresponding to the origin of my web-hook with serialized information needed to process in fact id of the row in the Database to make messages as light as possible.
Consumer
On the consumer side I create a consumer function for each origin queue and set the fetch policy to one to process message one by one in each queue finally I set the channel policy to wait an acknowledgement message before to send the next message . Wit this configuration consumers proceed message by message and solve the initial problem.
Implementation
Producer
async function create(){
await amqp.connect(RBMQ_CONNECTION_STRING).then(async (conn)=>{
await conn.createChannel().then(async (ch)=>{
global.channel_publisher=ch;
});
});
}
async function sendtask(queue,task){
if(!global.channel_publisher){
await create();
}
global.channel_publisher.assertQueue(queue).then((ok)=>{
global.channel_publisher.sendToQueue(queue, Buffer.from(task));
});
}
I use the sendtask(queue,task) function at the place I received my web-hook
Consumer
async function create(){
await amqp.connect(RBMQ_CONNECTION_STRING).then(async (conn)=>{
await conn.createChannel().then(async (ch)=>{
ch.prefetch(1);
global.channel_consumer=ch;
});
});
}
async function consumeTask(queue){
if(!global.channel_consumer){
await create();
}
global.channel_consumer.assertQueue(queue).then((ok)=>{
global.channel_consumer.consume(queue,(message)=>{
const args=message.content.toString().split(';');
await processWebhooks(args);
global.channel_consumer.ack(message);
});
});
}
I use the consumeTask(queue) when I had to process a new origin of web-hooks. Also I use it for initialize my application with all known origins in the database.

How to clean up AVCaptureSession in applicationDidEnterBackground?

I have an app that uses AVCaptureSession to process video. I like to write with zero memory leaks, and proper handling of all objects.
That's why this post - How to properly release an AVCaptureSession - was tremendously helpful - Since [session stopRunning] is asynchronous, you can't just stop the session and continue to release the holding object.
So that's solved. This is the code:
// Releases the object - used for late session cleanup
static void capture_cleanup(void* p)
{
CaptureScreenController* csc = (CaptureScreenController*)p;
[csc release]; // releases capture session if dealloc is called
}
// Stops the capture - this stops the capture, and upon stopping completion releases self.
- (void)stopCapture {
// Retain self, it will be released in capture_cleanup. This is to ensure cleanup is done properly,
// without the object being released in the middle of it.
[self retain];
// Stop the session
[session stopRunning];
// Add cleanup code when dispatch queue end
dispatch_queue_t queue = dispatch_queue_create("capture_screen", NULL);
dispatch_set_context(queue, self);
dispatch_set_finalizer_f(queue, capture_cleanup);
[dataOutput setSampleBufferDelegate: self queue: queue];
dispatch_release(queue);
}
Now I come to support app interruptions as a phone call, or pressing the home button. In case application enters background, I'd like to stop capturing, and pop my view controller.
I can't seem to do it at the applicationDidEnterBackground context. dealloc is never called, my object remains alive, and when I reopen the app the frames just start coming in automatically.
I tried using beginBackgroundTaskWithExpirationHandler but to no avail. It didn't change much.
Any suggestions?
Thanks!
I don't have an answer to your question.
But I also read the thread you mentioned and I'm trying to implement it.
I'm surprised you have this code in the stopCapture function:
// Add cleanup code when dispatch queue end
dispatch_queue_t queue = dispatch_queue_create("capture_screen", NULL);
dispatch_set_context(queue, self);
dispatch_set_finalizer_f(queue, capture_cleanup);
[dataOutput setSampleBufferDelegate: self queue: queue];
dispatch_release(queue);
I thought that code was required as part of the session initialization. Does this work for you?
Does your capture_cleanup function get called? mine isn't getting called and I'm trying to figure out why.

Resources