Coder Social home page Coder Social logo

Comments (16)

stevepryde avatar stevepryde commented on July 2, 2024 4

Documentation updated on main. Let me know if this is sufficient.

from thirtyfour.

TilBlechschmidt avatar TilBlechschmidt commented on July 2, 2024 3

I've run into a similar issue with one of my projects before. Not sure if it is the best solution but I just tokio::spawn'ed a closure which then takes care of dropping/finalising the value. Sure it is not guaranteed to run until completion if the process exits before it is done, but it is definitely better than a dead-lock.

(In the end, I implemented a process-wide job system which handles finalising of jobs including dropping values but that is totally overkill and unviable here)

from thirtyfour.

stevepryde avatar stevepryde commented on July 2, 2024 2

I only just read this now, but I have independently reached the same conclusion to remove the impl Drop entirely.
This change is already merged to main and the README has been updated. This will become v0.25.0 once the documentation is updated as you suggested.

I asked a question on the tokio repo yesterday regarding alternative solutions and came away from the discussion with the understanding that Rust's async really prefers you to be explicit about what you want to do and await any operations performed. This agrees with what you said about performing work such as network requests during a destructor being an anti-pattern. I see the point and it makes sense. It's just really uncomfortable to imagine trying to explain this to someone coming to Rust from another language. Then again, Rust often coerces you into certain patterns "for your own good" so maybe it's not that unusual.

I don't mind your suggestion about logging an error if WebDriver was dropped without calling quit(). That seems reasonable. Perhaps there also needs to be a way to silence the error in cases where it was intended? That can come later if people request it I guess. Adding documentation around this on the quit() method would also be worthwhile.

from thirtyfour.

TilBlechschmidt avatar TilBlechschmidt commented on July 2, 2024 1

The question we should probably ask ourselves here is whether a session should actively be closed when it is dropped.

  • From a pure library usability standpoint? Probably.
  • From a puristic standpoint? Likely not.

Let me explain: In general, I'd expect as few side-effects as possible when calling a function unless its signature or documentation makes it explicitly clear. The fact that a destructor performs asynchronous "side-effects" which — compounding the issue — make network calls, may be considered undesirable and somewhat of an anti-pattern.

Imagine PR #38 where a session is persisted to disk. This feature would be completely useless, when the destructor ALWAYS, inevitably, makes a network call to nuke the session.

So in that regard, I'd argue that it should be a deliberate act from the user to close the session and not something the destructor does.


Welcome to the flip side. From a "out-in-the-wild" perspective it obviously makes sense to automagically end the session when the linked object is dropped. Especially, since this tends to cause real headaches when people are using remote infrastructure for sessions and just occupy idle sessions because they forgot a driver.quit() (happens way too frequently, trust me).

So overall, the best middle ground might be to remove the driver.quit() from the impl Drop and instead just throw a big, red, blinking1 warning in the users face which reads something like:

Session dropped without calling .quit(), I hope you know what you are doing

Additionally, we could add a section in the documentation of WebDriver and the quit function.

1I may be exaggerating a bit, but you get the gist

from thirtyfour.

stevepryde avatar stevepryde commented on July 2, 2024

I'm able to reproduce the issue using your code. Thanks for providing that.
It seems to work when using the threaded scheduler but not the basic one.
I wonder if the futures::block_on method is trying to spawn a new task rather than run the async block to completion inline.

I had a lot of trouble with this method since async destructors are not currently supported in the language. There is an interesting blog post about it here: https://boats.gitlab.io/blog/post/poll-drop/.

I have tried using a tokio runtime inside this drop() method but tokio complains about creating a runtime within a runtime, which seems like an arbitrary restriction to me but maybe there is a good reason for it. Previously I have used an async_std runtime to do block_on() here, which worked fine but feels nasty. I'm not sure what futures::block_on does, but that seems to be the issue here.

Are you able to use the threaded_scheduler as a workaround for now?

from thirtyfour.

stevepryde avatar stevepryde commented on July 2, 2024

I've added another potential workaround - as of 0.9.1 you will be able to manually call driver.quit().await? and that should work fine.

from thirtyfour.

stevepryde avatar stevepryde commented on July 2, 2024

v0.9.1 is released now. Let me know if this helps.

I will leave this issue open since the underlying bug is still not resolved. Perhaps someone can provide a cleaner solution for this.

from thirtyfour.

jbernard002 avatar jbernard002 commented on July 2, 2024

I've update the code to version 0.9.1 and it's works fine.
Thanks a lots!

from thirtyfour.

stevepryde avatar stevepryde commented on July 2, 2024

If someone has a potential solution for this, a PR would be most welcome. Thanks!

from thirtyfour.

stevepryde avatar stevepryde commented on July 2, 2024

It should be possible to run a closure on a new executor and block, but I'm not sure why that doesn't work. It's possible that reqwest also spawns a new task internally? That would make it deadlock.

For now the workaround is to manually close the session just before the webdriver goes out of scope.

from thirtyfour.

stevepryde avatar stevepryde commented on July 2, 2024

I've done some more digging on this. Spawning a new task won't help unless you allow enough time for tokio to run that task. If your WebDriver goes out of scope at the end of your main function, you're probably out of luck. I don't really like this approach because it's really unclear if/when the browser will actually close. There's no way to access the join handle even if you wanted to.

I've also tried:

  • Creating another tokio runtime inside drop() - tokio prevents this
  • Using async_std::task::block_on - deadlock
  • Using futures::executor::block_on (the current approach) - deadlock

The deadlock only happens if using the basic (single-threaded) runtime. It may be related to the way reqwest manages its connection pool using spawned futures. I haven't tried any other async http clients.

The short story is that what we really want are async destructors, but who knows whether such a feature will ever exist in Rust.
For now, the workaround is to either use the multi-threaded scheduler or manually call WebDriver::quit().await.

from thirtyfour.

stevepryde avatar stevepryde commented on July 2, 2024

v0.25.0 released, which removes impl Drop. I haven't added any logging around forgetting to call quit(). I think that needs more discussion.

What should the message say? What log level? Is it really necessary?

from thirtyfour.

liranco avatar liranco commented on July 2, 2024

In my opinion, I don't think it's necessary to log it. If someone will expect the browser to close he'll probably search for a solution, so maybe it's better to document it instead :)

from thirtyfour.

TilBlechschmidt avatar TilBlechschmidt commented on July 2, 2024

I agree with the sentiment, especially because adding a log message would definitely require a method to suppress it for those few people intentionally omitting it.

However, especially for people new to Selenium, the fact that you explicitly have to quit the driver or else it will stick around for potentially a long time should not be neglected.

We should make sure that every example includes the explicit call to quit() with a comment above on why it is there. Additionally, a prominent section in the documentation (maybe even the readme) on why and when (basically always unless you exactly know what you are doing) you should call .quit() would be a good addition as well — especially since this explicit call is not required for every Selenium Library and some do it implicitly.

from thirtyfour.

liranco avatar liranco commented on July 2, 2024

Looks good to me 👍

from thirtyfour.

yerke avatar yerke commented on July 2, 2024

Drive by comment re the example in README: if driver.find() or elem_form.find() returns an error, then the driver.quit().await? will not be called. Maybe the example should be updated with a more robust approach?

from thirtyfour.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.