Skip to content
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

fix(api): call module functions across threads #5194

Merged
merged 3 commits into from
Mar 12, 2020
Merged

Conversation

sfoster1
Copy link
Member

@sfoster1 sfoster1 commented Mar 10, 2020

Moving to the threadmanager broke the case of calling module functions
through the threadmanager, since the module objects would be returned
directly.

They are now wrapped in CallBridger objects that have the
__getattribute__ override the same as thread managers but don't do the
actual thread managing. This is LRU cached so we don't build a bunch of
them.

Testing

  • Make sure that the module cards both in the pipettes and modules page and in protocol work
  • Note that sending module commands when a protocol is paused does not (and should not) work; fix(app): disable module commands when protocol paused #5209 will disable module commands when the protocol is paused. Module commands from the protocol run page should only be tested when the protocol is not running.

Moving to the threadmanager broke the case of calling module functions
through the threadmanager, since the module objects would be returned
directly.

They are now wrapped in CallBridger objects that have the
__getattribute__ override the same as thread managers but don't do the
actual thread managing. This is LRU cached so we don't build a bunch of
them.
@sfoster1 sfoster1 added api Affects the `api` project fix PR fixes a bug hmg hardware, motion, and geometry labels Mar 10, 2020
@sfoster1 sfoster1 requested review from a team March 10, 2020 17:10
@sfoster1 sfoster1 requested a review from a team as a code owner March 10, 2020 17:10
@sfoster1 sfoster1 self-assigned this Mar 10, 2020
@sfoster1 sfoster1 requested review from nusrat813 and removed request for a team March 10, 2020 17:10
@codecov
Copy link

codecov bot commented Mar 10, 2020

Codecov Report

Merging #5194 into edge will increase coverage by 0.50%.
The diff coverage is n/a.

Impacted file tree graph

@@            Coverage Diff             @@
##             edge    #5194      +/-   ##
==========================================
+ Coverage   60.29%   60.79%   +0.50%     
==========================================
  Files        1025     1025              
  Lines       29029    30132    +1103     
==========================================
+ Hits        17502    18320     +818     
- Misses      11527    11812     +285     
Impacted Files Coverage Δ
...t-server/robot_server/service/models/networking.py 92.00% <0.00%> (-4.35%) ⬇️
opentrons/hardware_control/types.py 98.00% <0.00%> (-2.00%) ⬇️
opentrons/hardware_control/execution_manager.py 94.44% <0.00%> (-1.21%) ⬇️
labware-library/src/labware-creator/index.js 0.00% <0.00%> (ø)
protocol-designer/src/components/FilePage.js 0.00% <0.00%> (ø)
app/src/components/ConfigurePipette/ConfigForm.js 0.00% <0.00%> (ø)
...bot-server/robot_server/service/models/settings.py 100.00% <0.00%> (ø)
...src/components/ConfigurePipette/ConfigFormGroup.js 0.00% <0.00%> (ø)
...components/AppSettings/AddManualIp/ManualIpForm.js 0.00% <0.00%> (ø)
...omponents/CalibrateLabware/ConfirmModalContents.js 0.00% <0.00%> (ø)
... and 17 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update cdf0c8a...f14c316. Read the comment docs.

Copy link
Contributor

@b-cooper b-cooper left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

functionality looks good, just a couple of questions.

@functools.lru_cache(8)
def wrap_module(
self, module: AbstractModule) -> CallBridger[AbstractModule]:
return CallBridger(module, self._loop)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For directness' sake, should this be object.__getattribute__(self, '_loop')?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

elif asyncio.iscoroutine(attr):
# Return awaitable coroutine properties run in managed thread/loop
fut = asyncio.run_coroutine_threadsafe(attr, loop)
wrapped = asyncio.wrap_future(fut, loop=asyncio.get_event_loop())
wrapped = asyncio.wrap_future(fut)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Before, this was failing in some contexts because the main thread didn't have an event loop. Do you have reason to believe this isn't an issue anymore?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you remember what those contexts were?

self.wrapped_obj = wrapped_obj
self._loop = loop

def __getattribute__(self, attr_name: str) -> Any:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As this is really identical to the __getattribute__ defined in the ThreadManager, and we don't want them to diverge individually, is there some way that they can both call a shared abstracted function to enforce this similarity?

WrappedObj = TypeVar('WrappedObj')


class CallBridger(Generic[WrappedObj]):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this another interface like ThreadManager that wraps an API object? What is aCallBridger?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is a threadmanager lite, I suppose. The ThreadManager is-a CallBridger, but it also is the actual owner of the thread. It could probably be changed to use the CallBridger

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a future state where calling methods on a HardwareController or API or whatever it is does not happen through a ThreadManager or CallBridger any other wrapper that hides the interface?

It's probably beyond the scope of this PR, but It's worth stating that the developer experience of using these wrappers is not pleasant.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is; it would be when the Smoothie serial driver and the module serial drivers are async. Right now, the serial transactions block and therefore block the event loop.

WrappedObj = TypeVar('WrappedObj')


class CallBridger(Generic[WrappedObj]):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a future state where calling methods on a HardwareController or API or whatever it is does not happen through a ThreadManager or CallBridger any other wrapper that hides the interface?

It's probably beyond the scope of this PR, but It's worth stating that the developer experience of using these wrappers is not pleasant.

Copy link

@nusrat813 nusrat813 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested, working as intended

Copy link
Contributor

@amitlissack amitlissack left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks

@sfoster1 sfoster1 merged commit ba1afe2 into edge Mar 12, 2020
@sfoster1 sfoster1 deleted the api_fix-module-execute branch March 12, 2020 13:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
api Affects the `api` project fix PR fixes a bug hmg hardware, motion, and geometry
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants