Merge lp:~raof/mir/no-ipc-on-compositor-threads into lp:mir
- no-ipc-on-compositor-threads
- Merge into development-branch
Status: | Merged |
---|---|
Approved by: | Alan Griffiths |
Approved revision: | no longer in the source branch. |
Merged at revision: | 4228 |
Proposed branch: | lp:~raof/mir/no-ipc-on-compositor-threads |
Merge into: | lp:mir |
Prerequisite: | lp:~raof/mir/better-buffer-plumbing |
Diff against target: |
1038 lines (+399/-182) 18 files modified
include/server/mir/frontend/buffer_stream.h (+0/-5) src/server/compositor/dropping_schedule.cpp (+0/-10) src/server/compositor/dropping_schedule.h (+0/-2) src/server/compositor/queueing_schedule.cpp (+0/-7) src/server/compositor/queueing_schedule.h (+0/-2) src/server/compositor/schedule.h (+0/-3) src/server/compositor/stream.cpp (+1/-33) src/server/compositor/stream.h (+0/-5) src/server/frontend/default_ipc_factory.cpp (+186/-1) src/server/frontend/default_ipc_factory.h (+4/-0) src/server/frontend/published_socket_connector.cpp (+50/-10) src/server/frontend/published_socket_connector.h (+1/-1) src/server/frontend/session_mediator.cpp (+47/-21) src/server/frontend/session_mediator.h (+6/-1) tests/include/mir/test/doubles/stub_buffer_stream.h (+0/-2) tests/unit-tests/compositor/test_multi_monitor_arbiter.cpp (+0/-6) tests/unit-tests/compositor/test_queueing_schedule.cpp (+0/-19) tests/unit-tests/frontend/test_session_mediator.cpp (+104/-54) |
To merge this branch: | bzr merge lp:~raof/mir/no-ipc-on-compositor-threads |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Alan Griffiths | Approve | ||
Mir CI Bot | continuous-integration | Approve | |
Review via email: mp+326827@code.launchpad.net |
Commit message
Move buffer-release IPC to a dedicated IPC thread.
Fixes: LP: #1395421
Description of the change
Mir CI Bot (mir-ci-bot) wrote : | # |
Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:4216
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:4216
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Mir CI Bot (mir-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:4217
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Chris Halse Rogers (raof) wrote : | # |
Ok. It's not clear to me how it was deadlocking, and I still can't reproduce this locally, so I'm going to hit rebuild on that. It *seems* that CI pretty reliably hits this, but let's check that it's not a fluke pass.
Mir CI Bot (mir-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:4217
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Chris Halse Rogers (raof) wrote : | # |
OK. So looks like that's good.
Alan Griffiths (alan-griffiths) wrote : | # |
Thanks for this - it's been on my to-tidy list far too long.
I'll withhold final approval until we've fixed the pre-requisite.
Mir CI Bot (mir-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:4218
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Alan Griffiths (alan-griffiths) wrote : | # |
Hmm, with more testing this may not be working right
Alan Griffiths (alan-griffiths) wrote : | # |
> Hmm, with more testing this may not be working right
Sorry not to be clear. Hitting EOD.
I tried building miral against this, running miral-app hosted by mir_demo_server and running all the clients.
When I came to close things down things were hung.
Didn't get time to be more specific.
Chris Halse Rogers (raof) wrote : | # |
Moar testing time! Thanks.
Chris Halse Rogers (raof) wrote : | # |
Hm.
To be clear, this is miral-shell hanging if the underlying mir_demo_server goes away (such as by SIGINT)?
If I quit miral-shell first everything seems to go as expected.
Chris Halse Rogers (raof) wrote : | # |
Oh, huh. Looks like it might be any EGL-using client...
Alan Griffiths (alan-griffiths) wrote : | # |
> Hm.
>
> To be clear, this is miral-shell hanging if the underlying mir_demo_server
> goes away (such as by SIGINT)?
>
> If I quit miral-shell first everything seems to go as expected.
No. I was just closing individual clients with Alt-F4 (or Alt-Shift-F4 for the few that don't listen).
I'll try to reproduce and narrow down exactly what went on. And check that it was actually this MP (and not something we already landed).
Chris Halse Rogers (raof) wrote : | # |
Hm. New hypothesis: this has always been broken, and doesn't require nesting.
It seems that if you start up any fullscreen client (tested with eglplasma and _target) before the throbber has finished then the throbber client hangs in swapbuffers and prevents miral-shell shutdown.
This also happens with the archive mir and miral.
Alan Griffiths (alan-griffiths) wrote : | # |
OK, I can't reproduce what I saw yesterday.
Maybe I hit an intermittent, maybe I had screwed up my test. Either way, let's land this and monitor.
Alan Griffiths (alan-griffiths) wrote : | # |
> Hm. New hypothesis: this has always been broken, and doesn't require nesting.
>
> It seems that if you start up any fullscreen client (tested with eglplasma and
> _target) before the throbber has finished then the throbber client hangs in
> swapbuffers and prevents miral-shell shutdown.
"throbber"?
> This also happens with the archive mir and miral.
File a bug. We should fix it.
Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Autolanding.
More details in the following jenkins job:
https:/
Executed test runs:
FAILURE: https:/
None: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Alan Griffiths (alan-griffiths) wrote : | # |
I don't think the failure is related.
Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Autolanding.
More details in the following jenkins job:
https:/
Executed test runs:
FAILURE: https:/
None: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
Chris Halse Rogers (raof) wrote : | # |
On Thu, Jul 20, 2017 at 6:44 PM, Alan Griffiths <email address hidden>
wrote:
>> Hm. New hypothesis: this has always been broken, and doesn't
>> require nesting.
>>
>> It seems that if you start up any fullscreen client (tested with
>> eglplasma and
>> _target) before the throbber has finished then the throbber client
>> hangs in
>> swapbuffers and prevents miral-shell shutdown.
>
> "throbber"?
The splashscreen, spinning Ubuntu logo thingy.
Chris Halse Rogers (raof) wrote : | # |
Hm. *Those* failures are due to real-time tests exceeding their thresholds. This branch plausibly increases the latency of those tests, as the buffer responses now require a context-switch rather than occurring on the compositor thread.
It might also just have been due to a transient load on the CI machine, so I'll give it another try...
Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Autolanding.
More details in the following jenkins job:
https:/
Executed test runs:
FAILURE: https:/
None: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Chris Halse Rogers (raof) wrote : | # |
(Spinner bug is bug #1705973)
Mir CI Bot (mir-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:4218
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Autolanding.
More details in the following jenkins job:
https:/
Executed test runs:
FAILURE: https:/
None: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Alan Griffiths (alan-griffiths) wrote : | # |
While this could change timing, I don't see why it should affect NestedServer.
Logged failure as lp:1706050 & re-approved
Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Autolanding.
More details in the following jenkins job:
https:/
Executed test runs:
FAILURE: https:/
None: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
Alan Griffiths (alan-griffiths) wrote : | # |
Now a failure in NestedServer.
...
12:28:26 terminate called after throwing an instance of 'boost:
12:28:26 what(): stop_server() failed to stop server
12:28:26 ==18875==
12:28:26 ==18875== Process terminating with default action of signal 6 (SIGABRT)
12:28:26 ==18875== at 0x458AEA9: raise (raise.c:54)
12:28:26 ==18875== by 0x458C406: abort (abort.c:89)
12:28:26 ==18875== by 0x4420D34: __gnu_cxx:
12:28:26 ==18875== by 0x441E832: ??? (in /usr/lib/
12:28:26 ==18875== by 0x441D648: ??? (in /usr/lib/
12:28:26 ==18875== by 0x441DE10: __gxx_personali
12:28:26 ==18875== by 0x453A3DE: ??? (in /lib/i386-
12:28:26 ==18875== by 0x453A856: _Unwind_Resume (in /lib/i386-
12:28:26 ==18875== by 0x8727D93: ~unique_lock (mutex:450)
12:28:26 ==18875== by 0x8727D93: mir_test_
...
I think that's (at least) a failure too many to be a coincidence.
Is this MP introducing a shutdown race that can deadlock?
Chris Halse Rogers (raof) wrote : | # |
I cannot for the life of me get this to fail locally.
I've run “make test” on a valgrind and debflags build in a loop for hours, and the tests continue to reliably pass.
Aaargh.
Here are some changes which also don't fail locally, but might help?
Mir CI Bot (mir-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:4220
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Mir CI Bot (mir-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:4223
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:4225
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Alan Griffiths (alan-griffiths) wrote : | # |
Just looking at the last commit:
1. Do we really want four shared pointers in ThreadExecutor? Surely one pointer holding a "shared state" implementation object would be better?
2. The (potential) problem with detaching threads is ensuring they exit before the process does. I don't see this being a problem here except possibly with valgrind...
Chris Halse Rogers (raof) wrote : | # |
It shouldn't matter if the threads don't end before the process does; valgrind doesn't count reachable objects as leaked, and if the thread hasn't finished then the objects are still reachable.
This should now *actually* work.
I'm somewhat surprised we haven't seen problems caused by the boost::...::socket outliving the io_service in the past. As far as I can tell, there's no mechanism to enforce that - we share SocketMessangers around pretty widely...
Mir CI Bot (mir-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:4227
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Alan Griffiths (alan-griffiths) wrote : | # |
It still feels untidy to exit while a detached thread might own resources, but that's probably less evil than the existing code.
Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Autolanding.
More details in the following jenkins job:
https:/
Executed test runs:
FAILURE: https:/
None: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Alan Griffiths (alan-griffiths) wrote : | # |
Hmm, this does look suspicious:
10:37:22 9: ==28377== 792 (24 direct, 768 indirect) bytes in 1 blocks are definitely lost in loss record 36 of 41
10:37:22 9: ==28377== at 0x4C2E19F: operator new(unsigned long) (in /usr/lib/
10:37:22 9: ==28377== by 0x51B754A: _S_make_
10:37:22 9: ==28377== by 0x51B754A: thread<(lambda at /<<BUILDDIR>
Chris Halse Rogers (raof) wrote : | # |
Damnit, valgrind!
Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:4228
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Chris Halse Rogers (raof) wrote : | # |
Oh, dear.
This is now running into poor interactions with fork(); specifically:
Specifically:
* After a fork() in a multithreaded program, the child can safely
call only async-signal-safe functions (see signal-safety(7)) until
such time as it calls execve(2).
When running “make test”, we've got a single process executing all the tests; since the ThreadExecutor is now static, we now have a thread waiting around before we get to fork().
This is why ptest doesn't fail; each test gets its own process...
Alan Griffiths (alan-griffiths) wrote : | # |
> Oh, dear.
>
> This is now running into poor interactions with fork(); specifically:
> Specifically:
> * After a fork() in a multithreaded program, the child can safely
> call only async-signal-safe functions (see signal-safety(7)) until
> such time as it calls execve(2).
>
> When running “make test”, we've got a single process executing all the tests;
> since the ThreadExecutor is now static, we now have a thread waiting around
> before we get to fork().
Ack. Objects with static duration can be problematic. :(
> This is why ptest doesn't fail; each test gets its own process...
I'm not convinced by that final reasoning: with ptest each test fixture gets its own process, but that still runs multiple tests (each of which may fork).
Chris Halse Rogers (raof) wrote : | # |
On 2 August 2017 6:03:59 pm AEST, Alan Griffiths <email address hidden> wrote:
>> This is why ptest doesn't fail; each test gets its own process...
>
>I'm not convinced by that final reasoning: with ptest each test fixture
>gets its own process, but that still runs multiple tests (each of which
>may fork).
More correctly: our tests that fork do so *first*, and do all of their multi-threading in a child.
Then along comes this MP, and causes a thread to hang around - waiting on a condition variable - from a previous test. Now our top-level fork is mt.
A hack - delete and recreate the executor across fork boundaries using pthread_atfork() - resolves this. It should also work to quiesce the executor pre-fork and then resume it in the other end.
--
Sent from my Android device with K-9 Mail. Please excuse my brevity.
Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:4229
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:4230
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Alan Griffiths (alan-griffiths) wrote : | # |
Just artful/
That's also happening in two other branches. (I've reproduced locally yet.)
Alan Griffiths (alan-griffiths) wrote : | # |
> Just artful/
>
> That's also happening in two other branches. (I've reproduced locally yet.)
I've *not* reproduced locally yet.
Chris Halse Rogers (raof) wrote : | # |
Yeah. *My* artful builds work fine!
Alan Griffiths (alan-griffiths) wrote : | # |
Hmm, the errors seem to be coming from DefaultPersiste
Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:4231
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:4232
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:4233
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:4234
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:4231
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Alan Griffiths (alan-griffiths) wrote : | # |
Alan Griffiths (alan-griffiths) wrote : | # |
+mir::Executor& system_executor()
+{
+ static std::once_flag setup;
+ static ThreadExecutor executor;
Hmm, as well as the fight you started with fork() how does this scale well with having multiple Mir server instances in process? (I can see it doesn't break the test suite, but hopefully that isn't "just luck".)
Alan Griffiths (alan-griffiths) wrote : | # |
make_socket_
"cotained"?
~~~~
What is the intention of "system_
Chris Halse Rogers (raof) wrote : | # |
system_executor() is named as such in honour of the C++ TS. I guess it shouldn't really be named that :)
This shouldn't be a problem for multiple servers in a single process - they'll all use the same IPC thread for buffer-return messages, but each buffer-return task should be extremely quick - almost just a syscall - and there shouldn't be all that many of them - they'll generally max out at 60/s/client.
If you run *lots* of really busy servers in the same process that might start getting awkward.
Alan Griffiths (alan-griffiths) wrote : | # |
Nit:
+ std::shared_
+ std::shared_
Did you have CLion set to the wrong "&" placement style?
Mir CI Bot (mir-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:4233
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Alan Griffiths (alan-griffiths) wrote : | # |
I think this is OK but in the "good old days" I would have wanted another set of eyes. (I did ask Alberto, but he's not found time - yet.)
Alan Griffiths (alan-griffiths) wrote : | # |
Running miral-desktop (trunk) on this leads to orphaned titlebars and a hung server.
I need to run some more tests to isolate the problem.
Alan Griffiths (alan-griffiths) wrote : | # |
$ sudo mir_demo_server --vt 4 --launch mir_demo_
Wait 30 seconds(ish)...
the server hangs
Alan Griffiths (alan-griffiths) wrote : | # |
> $ sudo mir_demo_server --vt 4 --launch mir_demo_
>
> Wait 30 seconds(ish)...
>
> the server hangs
Actually, the server isn't entirely hung - but killing the child process doesn't remove it from the scene.
Alan Griffiths (alan-griffiths) wrote : | # |
> > $ sudo mir_demo_server --vt 4 --launch mir_demo_
> >
> > Wait 30 seconds(ish)...
> >
> > the server hangs
>
> Actually, the server isn't entirely hung - but killing the child process
> doesn't remove it from the scene.
Specifically, the server has exhausted file handles: /proc/.../fd/ has lots like this:
lrwx------ 1 alan alan 64 Aug 15 12:03 999 -> /dev/shm/#128090392 (deleted)
Alan Griffiths (alan-griffiths) wrote : | # |
OK, I've made some progress:
multi_stream is hitting the problem because it repeatedly creates new BufferStreams and destroying the old ones (and RenderSurfaces).
When BufferStreams are created and destroyed like this the buffer_cache in SessionMediator grows intermittently but inexorably.
Up until the point when file handles are exhausted closing the connection releases the cache and the buffers. (Once file handles are exhausted the server struggles to release the connection.)
I've not yet got my head around where the buffers created for mir_buffer_
Chris Halse Rogers (raof) wrote : | # |
Thanks for that!
That made it obvious that we were never actually freeing the buffers allocated on a BufferStream when it is destroyed.
I thought that (a) the client side did that, and (b) the server side *also* did that.
But it turns out that stream-
Mir CI Bot (mir-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:4236
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Alan Griffiths (alan-griffiths) wrote : | # |
Looking good!
Preview Diff
1 | === modified file 'include/server/mir/frontend/buffer_stream.h' | |||
2 | --- include/server/mir/frontend/buffer_stream.h 2017-07-28 17:00:43 +0000 | |||
3 | +++ include/server/mir/frontend/buffer_stream.h 2017-08-16 07:06:44 +0000 | |||
4 | @@ -54,11 +54,6 @@ | |||
5 | 54 | 54 | ||
6 | 55 | virtual MirPixelFormat pixel_format() const = 0; | 55 | virtual MirPixelFormat pixel_format() const = 0; |
7 | 56 | 56 | ||
8 | 57 | //TODO: associate/disassociate_buffer are only used for timeout framedropping policy decisions. | ||
9 | 58 | // They will be removed once timeout framedropping policy moves to the client side. | ||
10 | 59 | virtual void associate_buffer(graphics::BufferID) = 0; | ||
11 | 60 | virtual void disassociate_buffer(graphics::BufferID) = 0; | ||
12 | 61 | |||
13 | 62 | //TODO: framedropping for swapinterval-0 can probably be effectively managed from the client | 57 | //TODO: framedropping for swapinterval-0 can probably be effectively managed from the client |
14 | 63 | // side once we only support the NBS system. | 58 | // side once we only support the NBS system. |
15 | 64 | virtual void allow_framedropping(bool) = 0; | 59 | virtual void allow_framedropping(bool) = 0; |
16 | 65 | 60 | ||
17 | === modified file 'src/server/compositor/dropping_schedule.cpp' | |||
18 | --- src/server/compositor/dropping_schedule.cpp 2017-07-28 17:00:43 +0000 | |||
19 | +++ src/server/compositor/dropping_schedule.cpp 2017-08-16 07:06:44 +0000 | |||
20 | @@ -29,18 +29,8 @@ | |||
21 | 29 | 29 | ||
22 | 30 | void mc::DroppingSchedule::schedule(std::shared_ptr<mg::Buffer> const& buffer) | 30 | void mc::DroppingSchedule::schedule(std::shared_ptr<mg::Buffer> const& buffer) |
23 | 31 | { | 31 | { |
24 | 32 | auto drop = schedule_nonblocking(buffer); | ||
25 | 33 | if (drop.valid()) | ||
26 | 34 | drop.wait(); | ||
27 | 35 | } | ||
28 | 36 | |||
29 | 37 | std::future<void> mc::DroppingSchedule::schedule_nonblocking( | ||
30 | 38 | std::shared_ptr<mg::Buffer> const& buffer) | ||
31 | 39 | { | ||
32 | 40 | std::future<void> drop; | ||
33 | 41 | std::lock_guard<decltype(mutex)> lk(mutex); | 32 | std::lock_guard<decltype(mutex)> lk(mutex); |
34 | 42 | the_only_buffer = buffer; | 33 | the_only_buffer = buffer; |
35 | 43 | return drop; | ||
36 | 44 | } | 34 | } |
37 | 45 | 35 | ||
38 | 46 | unsigned int mc::DroppingSchedule::num_scheduled() | 36 | unsigned int mc::DroppingSchedule::num_scheduled() |
39 | 47 | 37 | ||
40 | === modified file 'src/server/compositor/dropping_schedule.h' | |||
41 | --- src/server/compositor/dropping_schedule.h 2017-07-28 17:00:43 +0000 | |||
42 | +++ src/server/compositor/dropping_schedule.h 2017-08-16 07:06:44 +0000 | |||
43 | @@ -32,8 +32,6 @@ | |||
44 | 32 | public: | 32 | public: |
45 | 33 | DroppingSchedule(); | 33 | DroppingSchedule(); |
46 | 34 | void schedule(std::shared_ptr<graphics::Buffer> const& buffer) override; | 34 | void schedule(std::shared_ptr<graphics::Buffer> const& buffer) override; |
47 | 35 | std::future<void> schedule_nonblocking( | ||
48 | 36 | std::shared_ptr<graphics::Buffer> const& buffer) override; | ||
49 | 37 | unsigned int num_scheduled() override; | 35 | unsigned int num_scheduled() override; |
50 | 38 | std::shared_ptr<graphics::Buffer> next_buffer() override; | 36 | std::shared_ptr<graphics::Buffer> next_buffer() override; |
51 | 39 | 37 | ||
52 | 40 | 38 | ||
53 | === modified file 'src/server/compositor/queueing_schedule.cpp' | |||
54 | --- src/server/compositor/queueing_schedule.cpp 2017-07-28 17:00:43 +0000 | |||
55 | +++ src/server/compositor/queueing_schedule.cpp 2017-08-16 07:06:44 +0000 | |||
56 | @@ -32,13 +32,6 @@ | |||
57 | 32 | queue.emplace_back(buffer); | 32 | queue.emplace_back(buffer); |
58 | 33 | } | 33 | } |
59 | 34 | 34 | ||
60 | 35 | std::future<void> mc::QueueingSchedule::schedule_nonblocking( | ||
61 | 36 | std::shared_ptr<graphics::Buffer> const& buffer) | ||
62 | 37 | { | ||
63 | 38 | schedule(buffer); | ||
64 | 39 | return {}; | ||
65 | 40 | } | ||
66 | 41 | |||
67 | 42 | unsigned int mc::QueueingSchedule::num_scheduled() | 35 | unsigned int mc::QueueingSchedule::num_scheduled() |
68 | 43 | { | 36 | { |
69 | 44 | std::lock_guard<decltype(mutex)> lk(mutex); | 37 | std::lock_guard<decltype(mutex)> lk(mutex); |
70 | 45 | 38 | ||
71 | === modified file 'src/server/compositor/queueing_schedule.h' | |||
72 | --- src/server/compositor/queueing_schedule.h 2017-07-28 17:00:43 +0000 | |||
73 | +++ src/server/compositor/queueing_schedule.h 2017-08-16 07:06:44 +0000 | |||
74 | @@ -32,8 +32,6 @@ | |||
75 | 32 | { | 32 | { |
76 | 33 | public: | 33 | public: |
77 | 34 | void schedule(std::shared_ptr<graphics::Buffer> const& buffer) override; | 34 | void schedule(std::shared_ptr<graphics::Buffer> const& buffer) override; |
78 | 35 | std::future<void> schedule_nonblocking( | ||
79 | 36 | std::shared_ptr<graphics::Buffer> const& buffer) override; | ||
80 | 37 | unsigned int num_scheduled() override; | 35 | unsigned int num_scheduled() override; |
81 | 38 | std::shared_ptr<graphics::Buffer> next_buffer() override; | 36 | std::shared_ptr<graphics::Buffer> next_buffer() override; |
82 | 39 | 37 | ||
83 | 40 | 38 | ||
84 | === modified file 'src/server/compositor/schedule.h' | |||
85 | --- src/server/compositor/schedule.h 2017-07-28 17:00:43 +0000 | |||
86 | +++ src/server/compositor/schedule.h 2017-08-16 07:06:44 +0000 | |||
87 | @@ -19,7 +19,6 @@ | |||
88 | 19 | #define MIR_COMPOSITOR_SCHEDULE_H_ | 19 | #define MIR_COMPOSITOR_SCHEDULE_H_ |
89 | 20 | 20 | ||
90 | 21 | #include <memory> | 21 | #include <memory> |
91 | 22 | #include <future> | ||
92 | 23 | 22 | ||
93 | 24 | namespace mir | 23 | namespace mir |
94 | 25 | { | 24 | { |
95 | @@ -31,8 +30,6 @@ | |||
96 | 31 | { | 30 | { |
97 | 32 | public: | 31 | public: |
98 | 33 | virtual void schedule(std::shared_ptr<graphics::Buffer> const& buffer) = 0; | 32 | virtual void schedule(std::shared_ptr<graphics::Buffer> const& buffer) = 0; |
99 | 34 | virtual std::future<void> schedule_nonblocking( | ||
100 | 35 | std::shared_ptr<graphics::Buffer> const& buffer) = 0; | ||
101 | 36 | virtual unsigned int num_scheduled() = 0; | 33 | virtual unsigned int num_scheduled() = 0; |
102 | 37 | virtual std::shared_ptr<graphics::Buffer> next_buffer() = 0; | 34 | virtual std::shared_ptr<graphics::Buffer> next_buffer() = 0; |
103 | 38 | 35 | ||
104 | 39 | 36 | ||
105 | === modified file 'src/server/compositor/stream.cpp' | |||
106 | --- src/server/compositor/stream.cpp 2017-07-28 17:00:43 +0000 | |||
107 | +++ src/server/compositor/stream.cpp 2017-08-16 07:06:44 +0000 | |||
108 | @@ -48,18 +48,8 @@ | |||
109 | 48 | 48 | ||
110 | 49 | mc::Stream::~Stream() = default; | 49 | mc::Stream::~Stream() = default; |
111 | 50 | 50 | ||
112 | 51 | unsigned int mc::Stream::client_owned_buffer_count(std::lock_guard<decltype(mutex)> const&) const | ||
113 | 52 | { | ||
114 | 53 | auto server_count = schedule->num_scheduled(); | ||
115 | 54 | if (arbiter->has_buffer()) | ||
116 | 55 | server_count++; | ||
117 | 56 | return associated_buffers.size() - server_count; | ||
118 | 57 | } | ||
119 | 58 | |||
120 | 59 | void mc::Stream::submit_buffer(std::shared_ptr<mg::Buffer> const& buffer) | 51 | void mc::Stream::submit_buffer(std::shared_ptr<mg::Buffer> const& buffer) |
121 | 60 | { | 52 | { |
122 | 61 | std::future<void> deferred_io; | ||
123 | 62 | |||
124 | 63 | if (!buffer) | 53 | if (!buffer) |
125 | 64 | BOOST_THROW_EXCEPTION(std::invalid_argument("cannot submit null buffer")); | 54 | BOOST_THROW_EXCEPTION(std::invalid_argument("cannot submit null buffer")); |
126 | 65 | 55 | ||
127 | @@ -67,17 +57,9 @@ | |||
128 | 67 | std::lock_guard<decltype(mutex)> lk(mutex); | 57 | std::lock_guard<decltype(mutex)> lk(mutex); |
129 | 68 | first_frame_posted = true; | 58 | first_frame_posted = true; |
130 | 69 | pf = buffer->pixel_format(); | 59 | pf = buffer->pixel_format(); |
132 | 70 | deferred_io = schedule->schedule_nonblocking(buffer); | 60 | schedule->schedule(buffer); |
133 | 71 | } | 61 | } |
134 | 72 | observers.frame_posted(1, buffer->size()); | 62 | observers.frame_posted(1, buffer->size()); |
135 | 73 | |||
136 | 74 | // Ensure that mutex is not locked while we do this (synchronous!) socket | ||
137 | 75 | // IO. Holding it locked blocks the compositor thread(s) from rendering. | ||
138 | 76 | if (deferred_io.valid()) | ||
139 | 77 | { | ||
140 | 78 | // TODO: Throttling of GPU hogs goes here (LP: #1211700, LP: #1665802) | ||
141 | 79 | deferred_io.wait(); | ||
142 | 80 | } | ||
143 | 81 | } | 63 | } |
144 | 82 | 64 | ||
145 | 83 | void mc::Stream::with_most_recent_buffer_do(std::function<void(mg::Buffer&)> const& fn) | 65 | void mc::Stream::with_most_recent_buffer_do(std::function<void(mg::Buffer&)> const& fn) |
146 | @@ -184,20 +166,6 @@ | |||
147 | 184 | return first_frame_posted; | 166 | return first_frame_posted; |
148 | 185 | } | 167 | } |
149 | 186 | 168 | ||
150 | 187 | void mc::Stream::associate_buffer(mg::BufferID id) | ||
151 | 188 | { | ||
152 | 189 | std::lock_guard<decltype(mutex)> lk(mutex); | ||
153 | 190 | associated_buffers.insert(id); | ||
154 | 191 | } | ||
155 | 192 | |||
156 | 193 | void mc::Stream::disassociate_buffer(mg::BufferID id) | ||
157 | 194 | { | ||
158 | 195 | std::lock_guard<decltype(mutex)> lk(mutex); | ||
159 | 196 | auto it = associated_buffers.find(id); | ||
160 | 197 | if (it != associated_buffers.end()) | ||
161 | 198 | associated_buffers.erase(it); | ||
162 | 199 | } | ||
163 | 200 | |||
164 | 201 | void mc::Stream::set_scale(float) | 169 | void mc::Stream::set_scale(float) |
165 | 202 | { | 170 | { |
166 | 203 | } | 171 | } |
167 | 204 | 172 | ||
168 | === modified file 'src/server/compositor/stream.h' | |||
169 | --- src/server/compositor/stream.h 2017-07-28 17:00:43 +0000 | |||
170 | +++ src/server/compositor/stream.h 2017-08-16 07:06:44 +0000 | |||
171 | @@ -56,8 +56,6 @@ | |||
172 | 56 | int buffers_ready_for_compositor(void const* user_id) const override; | 56 | int buffers_ready_for_compositor(void const* user_id) const override; |
173 | 57 | void drop_old_buffers() override; | 57 | void drop_old_buffers() override; |
174 | 58 | bool has_submitted_buffer() const override; | 58 | bool has_submitted_buffer() const override; |
175 | 59 | void associate_buffer(graphics::BufferID) override; | ||
176 | 60 | void disassociate_buffer(graphics::BufferID) override; | ||
177 | 61 | void set_scale(float scale) override; | 59 | void set_scale(float scale) override; |
178 | 62 | 60 | ||
179 | 63 | private: | 61 | private: |
180 | @@ -73,9 +71,6 @@ | |||
181 | 73 | bool first_frame_posted; | 71 | bool first_frame_posted; |
182 | 74 | 72 | ||
183 | 75 | scene::SurfaceObservers observers; | 73 | scene::SurfaceObservers observers; |
184 | 76 | |||
185 | 77 | std::set<graphics::BufferID> associated_buffers; | ||
186 | 78 | unsigned int client_owned_buffer_count(std::lock_guard<decltype(mutex)> const&) const; | ||
187 | 79 | }; | 74 | }; |
188 | 80 | } | 75 | } |
189 | 81 | } | 76 | } |
190 | 82 | 77 | ||
191 | === modified file 'src/server/frontend/default_ipc_factory.cpp' | |||
192 | --- src/server/frontend/default_ipc_factory.cpp 2017-07-28 17:00:43 +0000 | |||
193 | +++ src/server/frontend/default_ipc_factory.cpp 2017-08-16 07:06:44 +0000 | |||
194 | @@ -29,12 +29,196 @@ | |||
195 | 29 | #include "event_sink_factory.h" | 29 | #include "event_sink_factory.h" |
196 | 30 | #include "mir/graphics/graphic_buffer_allocator.h" | 30 | #include "mir/graphics/graphic_buffer_allocator.h" |
197 | 31 | #include "mir/cookie/authority.h" | 31 | #include "mir/cookie/authority.h" |
198 | 32 | #include "mir/executor.h" | ||
199 | 33 | #include "mir/signal_blocker.h" | ||
200 | 34 | |||
201 | 35 | #include <deque> | ||
202 | 32 | 36 | ||
203 | 33 | namespace mf = mir::frontend; | 37 | namespace mf = mir::frontend; |
204 | 34 | namespace mg = mir::graphics; | 38 | namespace mg = mir::graphics; |
205 | 35 | namespace mi = mir::input; | 39 | namespace mi = mir::input; |
206 | 36 | namespace ms = mir::scene; | 40 | namespace ms = mir::scene; |
207 | 37 | 41 | ||
208 | 42 | namespace | ||
209 | 43 | { | ||
210 | 44 | class ThreadExecutor : public mir::Executor | ||
211 | 45 | { | ||
212 | 46 | public: | ||
213 | 47 | ThreadExecutor() = default; | ||
214 | 48 | |||
215 | 49 | ThreadExecutor(ThreadExecutor const&) = delete; | ||
216 | 50 | ThreadExecutor& operator=(ThreadExecutor const&) = delete; | ||
217 | 51 | |||
218 | 52 | void do_work() noexcept | ||
219 | 53 | { | ||
220 | 54 | std::unique_lock<std::mutex> lock{queue_mutex}; | ||
221 | 55 | for(;;) | ||
222 | 56 | { | ||
223 | 57 | while (!tasks.empty()) | ||
224 | 58 | { | ||
225 | 59 | std::function<void()> task; | ||
226 | 60 | task = std::move(tasks.front()); | ||
227 | 61 | tasks.pop_front(); | ||
228 | 62 | |||
229 | 63 | lock.unlock(); | ||
230 | 64 | task(); | ||
231 | 65 | /* | ||
232 | 66 | * The task functor may have captured resources with non-trivial | ||
233 | 67 | * destructors. | ||
234 | 68 | * | ||
235 | 69 | * Ensure those destructors are called outside the lock. | ||
236 | 70 | */ | ||
237 | 71 | task = nullptr; | ||
238 | 72 | lock.lock(); | ||
239 | 73 | } | ||
240 | 74 | |||
241 | 75 | if (state != State::Running) | ||
242 | 76 | { | ||
243 | 77 | return; | ||
244 | 78 | } | ||
245 | 79 | |||
246 | 80 | queue_notifier.wait( | ||
247 | 81 | lock, | ||
248 | 82 | [this]() | ||
249 | 83 | { | ||
250 | 84 | return (state != State::Running) || !tasks.empty(); | ||
251 | 85 | }); | ||
252 | 86 | } | ||
253 | 87 | } | ||
254 | 88 | |||
255 | 89 | ~ThreadExecutor() | ||
256 | 90 | { | ||
257 | 91 | quiesce(); | ||
258 | 92 | } | ||
259 | 93 | |||
260 | 94 | void spawn(std::function<void()>&& work) override | ||
261 | 95 | { | ||
262 | 96 | { | ||
263 | 97 | std::lock_guard<std::mutex> lock{queue_mutex}; | ||
264 | 98 | tasks.emplace_back(std::move(work)); | ||
265 | 99 | |||
266 | 100 | if (state == State::NotYetStarted) | ||
267 | 101 | { | ||
268 | 102 | /* | ||
269 | 103 | * Block all signals on the dispatch thread. | ||
270 | 104 | * | ||
271 | 105 | * Threads inherit their parent's signal mask, so use a SignalBlocker to block | ||
272 | 106 | * all signals *before* spawning the thread (and then restore the signal mask | ||
273 | 107 | * when this constructor completes). | ||
274 | 108 | */ | ||
275 | 109 | mir::SignalBlocker blocker; | ||
276 | 110 | state = State::Running; | ||
277 | 111 | dispatch_thread = std::thread{std::bind(&ThreadExecutor::do_work, this)}; | ||
278 | 112 | } | ||
279 | 113 | } | ||
280 | 114 | queue_notifier.notify_all(); | ||
281 | 115 | } | ||
282 | 116 | |||
283 | 117 | void quiesce() | ||
284 | 118 | { | ||
285 | 119 | { | ||
286 | 120 | std::lock_guard<std::mutex> lock{queue_mutex}; | ||
287 | 121 | state = State::Quiesced; | ||
288 | 122 | } | ||
289 | 123 | queue_notifier.notify_all(); | ||
290 | 124 | |||
291 | 125 | if (dispatch_thread.joinable()) | ||
292 | 126 | dispatch_thread.join(); | ||
293 | 127 | } | ||
294 | 128 | |||
295 | 129 | void resume() | ||
296 | 130 | { | ||
297 | 131 | std::lock_guard<std::mutex> lock{queue_mutex}; | ||
298 | 132 | state = State::NotYetStarted; | ||
299 | 133 | if (!tasks.empty()) | ||
300 | 134 | { | ||
301 | 135 | /* | ||
302 | 136 | * Block all signals on the dispatch thread. | ||
303 | 137 | * | ||
304 | 138 | * Threads inherit their parent's signal mask, so use a SignalBlocker to block | ||
305 | 139 | * all signals *before* spawning the thread (and then restore the signal mask | ||
306 | 140 | * when this constructor completes). | ||
307 | 141 | */ | ||
308 | 142 | mir::SignalBlocker blocker; | ||
309 | 143 | state = State::Running; | ||
310 | 144 | dispatch_thread = std::thread{std::bind(&ThreadExecutor::do_work, this)}; | ||
311 | 145 | } | ||
312 | 146 | } | ||
313 | 147 | |||
314 | 148 | void discard() | ||
315 | 149 | { | ||
316 | 150 | std::lock_guard<std::mutex> lock{queue_mutex}; | ||
317 | 151 | tasks.clear(); | ||
318 | 152 | state = State::NotYetStarted; | ||
319 | 153 | } | ||
320 | 154 | private: | ||
321 | 155 | std::thread dispatch_thread; | ||
322 | 156 | |||
323 | 157 | std::mutex queue_mutex; | ||
324 | 158 | std::condition_variable queue_notifier; | ||
325 | 159 | enum class State | ||
326 | 160 | { | ||
327 | 161 | NotYetStarted, | ||
328 | 162 | Running, | ||
329 | 163 | Quiesced | ||
330 | 164 | } state{State::NotYetStarted}; | ||
331 | 165 | std::deque<std::function<void()>> tasks; | ||
332 | 166 | }; | ||
333 | 167 | |||
334 | 168 | mir::Executor& buffer_return_ipc_executor() | ||
335 | 169 | { | ||
336 | 170 | static std::once_flag setup; | ||
337 | 171 | static ThreadExecutor executor; | ||
338 | 172 | |||
339 | 173 | std::call_once( | ||
340 | 174 | setup, | ||
341 | 175 | []() | ||
342 | 176 | { | ||
343 | 177 | /* | ||
344 | 178 | * fork() interacts with threads by screaming about being | ||
345 | 179 | * smothered by moths and then gibbering in a corner. | ||
346 | 180 | * | ||
347 | 181 | * Conveniently, our test-suite makes extensive use of fork(), | ||
348 | 182 | * and runs all the tests from a single main process, meaning | ||
349 | 183 | * that once a single test has called executor.spawn() every | ||
350 | 184 | * subsequent call to fork() is a call from a multithreaded | ||
351 | 185 | * program. | ||
352 | 186 | * | ||
353 | 187 | * Enter the moths. | ||
354 | 188 | * | ||
355 | 189 | * We can get around this by quiescing the executor; temporarily | ||
356 | 190 | * halting its execution thread and then resuming it post-fork. | ||
357 | 191 | */ | ||
358 | 192 | pthread_atfork( | ||
359 | 193 | []() | ||
360 | 194 | { | ||
361 | 195 | // Pre-fork | ||
362 | 196 | executor.quiesce(); | ||
363 | 197 | }, | ||
364 | 198 | []() | ||
365 | 199 | { | ||
366 | 200 | /* | ||
367 | 201 | * Post-fork, in the parent: | ||
368 | 202 | * Resume execution, executing any functors queued | ||
369 | 203 | * since quiescence. | ||
370 | 204 | */ | ||
371 | 205 | executor.resume(); | ||
372 | 206 | }, | ||
373 | 207 | []() | ||
374 | 208 | { | ||
375 | 209 | /* | ||
376 | 210 | * Post-fork, in the child: | ||
377 | 211 | * Discard any tasks that snuck in after pre-fork but before fork(); | ||
378 | 212 | * they'll be handled in the parent. | ||
379 | 213 | */ | ||
380 | 214 | executor.discard(); | ||
381 | 215 | }); | ||
382 | 216 | }); | ||
383 | 217 | |||
384 | 218 | return executor; | ||
385 | 219 | } | ||
386 | 220 | } | ||
387 | 221 | |||
388 | 38 | mf::DefaultIpcFactory::DefaultIpcFactory( | 222 | mf::DefaultIpcFactory::DefaultIpcFactory( |
389 | 39 | std::shared_ptr<Shell> const& shell, | 223 | std::shared_ptr<Shell> const& shell, |
390 | 40 | std::shared_ptr<SessionMediatorObserver> const& sm_observer, | 224 | std::shared_ptr<SessionMediatorObserver> const& sm_observer, |
391 | @@ -152,5 +336,6 @@ | |||
392 | 152 | cookie_authority, | 336 | cookie_authority, |
393 | 153 | input_changer, | 337 | input_changer, |
394 | 154 | extensions, | 338 | extensions, |
396 | 155 | buffer_allocator); | 339 | buffer_allocator, |
397 | 340 | buffer_return_ipc_executor()); | ||
398 | 156 | } | 341 | } |
399 | 157 | 342 | ||
400 | === modified file 'src/server/frontend/default_ipc_factory.h' | |||
401 | --- src/server/frontend/default_ipc_factory.h 2017-07-28 17:00:43 +0000 | |||
402 | +++ src/server/frontend/default_ipc_factory.h 2017-08-16 07:06:44 +0000 | |||
403 | @@ -24,6 +24,9 @@ | |||
404 | 24 | 24 | ||
405 | 25 | namespace mir | 25 | namespace mir |
406 | 26 | { | 26 | { |
407 | 27 | |||
408 | 28 | class Executor; | ||
409 | 29 | |||
410 | 27 | namespace cookie | 30 | namespace cookie |
411 | 28 | { | 31 | { |
412 | 29 | class Authority; | 32 | class Authority; |
413 | @@ -109,6 +112,7 @@ | |||
414 | 109 | std::shared_ptr<cookie::Authority> const cookie_authority; | 112 | std::shared_ptr<cookie::Authority> const cookie_authority; |
415 | 110 | std::shared_ptr<InputConfigurationChanger> const input_changer; | 113 | std::shared_ptr<InputConfigurationChanger> const input_changer; |
416 | 111 | std::vector<mir::ExtensionDescription> const extensions; | 114 | std::vector<mir::ExtensionDescription> const extensions; |
417 | 115 | std::shared_ptr<mir::Executor> const execution_queue; | ||
418 | 112 | }; | 116 | }; |
419 | 113 | } | 117 | } |
420 | 114 | } | 118 | } |
421 | 115 | 119 | ||
422 | === modified file 'src/server/frontend/published_socket_connector.cpp' | |||
423 | --- src/server/frontend/published_socket_connector.cpp 2017-08-15 10:04:24 +0000 | |||
424 | +++ src/server/frontend/published_socket_connector.cpp 2017-08-16 07:06:44 +0000 | |||
425 | @@ -95,6 +95,30 @@ | |||
426 | 95 | } | 95 | } |
427 | 96 | return socket_name; | 96 | return socket_name; |
428 | 97 | } | 97 | } |
429 | 98 | |||
430 | 99 | std::shared_ptr<boost::asio::local::stream_protocol::socket> make_socket_self_contained( | ||
431 | 100 | std::shared_ptr<boost::asio::io_service> const &io_service, | ||
432 | 101 | std::shared_ptr<boost::asio::local::stream_protocol::socket> const &socket) | ||
433 | 102 | { | ||
434 | 103 | struct SelfContainedSocket { | ||
435 | 104 | SelfContainedSocket( | ||
436 | 105 | std::shared_ptr<boost::asio::io_service> const& io_service, | ||
437 | 106 | std::shared_ptr<boost::asio::local::stream_protocol::socket> const& socket) | ||
438 | 107 | : io_service{io_service}, | ||
439 | 108 | socket{socket} | ||
440 | 109 | { | ||
441 | 110 | } | ||
442 | 111 | |||
443 | 112 | std::shared_ptr<boost::asio::io_service> const io_service; | ||
444 | 113 | std::shared_ptr<boost::asio::local::stream_protocol::socket> const socket; | ||
445 | 114 | }; | ||
446 | 115 | |||
447 | 116 | auto holder = std::make_shared<SelfContainedSocket>(io_service, socket); | ||
448 | 117 | |||
449 | 118 | return std::shared_ptr<boost::asio::local::stream_protocol::socket>( | ||
450 | 119 | holder, | ||
451 | 120 | holder->socket.get()); | ||
452 | 121 | } | ||
453 | 98 | } | 122 | } |
454 | 99 | 123 | ||
455 | 100 | mf::PublishedSocketConnector::PublishedSocketConnector( | 124 | mf::PublishedSocketConnector::PublishedSocketConnector( |
456 | @@ -104,7 +128,7 @@ | |||
457 | 104 | std::shared_ptr<ConnectorReport> const& report) | 128 | std::shared_ptr<ConnectorReport> const& report) |
458 | 105 | : BasicConnector(connection_creator, report), | 129 | : BasicConnector(connection_creator, report), |
459 | 106 | socket_file(remove_if_stale(socket_file)), | 130 | socket_file(remove_if_stale(socket_file)), |
461 | 107 | acceptor(io_service, socket_file) | 131 | acceptor(*io_service, socket_file) |
462 | 108 | { | 132 | { |
463 | 109 | emergency_cleanup_registry.add( | 133 | emergency_cleanup_registry.add( |
464 | 110 | [socket_file] { std::remove(socket_file.c_str()); }); | 134 | [socket_file] { std::remove(socket_file.c_str()); }); |
465 | @@ -120,13 +144,26 @@ | |||
466 | 120 | { | 144 | { |
467 | 121 | report->listening_on(socket_file); | 145 | report->listening_on(socket_file); |
468 | 122 | 146 | ||
470 | 123 | auto socket = std::make_shared<boost::asio::local::stream_protocol::socket>(io_service); | 147 | auto socket = std::make_shared<boost::asio::local::stream_protocol::socket>(*io_service); |
471 | 124 | 148 | ||
472 | 125 | acceptor.async_accept( | 149 | acceptor.async_accept( |
473 | 126 | *socket, | 150 | *socket, |
475 | 127 | [this,socket](boost::system::error_code const& ec) | 151 | [ |
476 | 152 | this, | ||
477 | 153 | socket, | ||
478 | 154 | maybe_service = std::weak_ptr<boost::asio::io_service>(io_service) | ||
479 | 155 | ](boost::system::error_code const& ec) | ||
480 | 128 | { | 156 | { |
482 | 129 | on_new_connection(socket, ec); | 157 | /* |
483 | 158 | * This functor lives on a queue in the io_service. If we capture a strong | ||
484 | 159 | * reference to the io_service, we'll have a reference cycle. | ||
485 | 160 | * | ||
486 | 161 | * Neither io_service::reset() nor acceptor.close() appear to cause the | ||
487 | 162 | * destruction of the functor, so just take a weak reference to the | ||
488 | 163 | * io_service and upgrade it when something actually connects. | ||
489 | 164 | */ | ||
490 | 165 | if (auto live_service = maybe_service.lock()) | ||
491 | 166 | on_new_connection(make_socket_self_contained(live_service, socket), ec); | ||
492 | 130 | }); | 167 | }); |
493 | 131 | } | 168 | } |
494 | 132 | 169 | ||
495 | @@ -148,7 +185,8 @@ | |||
496 | 148 | mf::BasicConnector::BasicConnector( | 185 | mf::BasicConnector::BasicConnector( |
497 | 149 | std::shared_ptr<ConnectionCreator> const& connection_creator, | 186 | std::shared_ptr<ConnectionCreator> const& connection_creator, |
498 | 150 | std::shared_ptr<ConnectorReport> const& report) | 187 | std::shared_ptr<ConnectorReport> const& report) |
500 | 151 | : work(io_service), | 188 | : io_service(std::make_shared<boost::asio::io_service>()), |
501 | 189 | work(*io_service), | ||
502 | 152 | report(report), | 190 | report(report), |
503 | 153 | connection_creator{connection_creator} | 191 | connection_creator{connection_creator} |
504 | 154 | { | 192 | { |
505 | @@ -163,7 +201,7 @@ | |||
506 | 163 | try | 201 | try |
507 | 164 | { | 202 | { |
508 | 165 | report->thread_start(); | 203 | report->thread_start(); |
510 | 166 | io_service.run(); | 204 | io_service->run(); |
511 | 167 | report->thread_end(); | 205 | report->thread_end(); |
512 | 168 | return; | 206 | return; |
513 | 169 | } | 207 | } |
514 | @@ -179,14 +217,14 @@ | |||
515 | 179 | void mf::BasicConnector::stop() | 217 | void mf::BasicConnector::stop() |
516 | 180 | { | 218 | { |
517 | 181 | /* Stop processing new requests */ | 219 | /* Stop processing new requests */ |
519 | 182 | io_service.stop(); | 220 | io_service->stop(); |
520 | 183 | 221 | ||
521 | 184 | /* Wait for io processing thread to finish */ | 222 | /* Wait for io processing thread to finish */ |
522 | 185 | if (io_service_thread.joinable()) | 223 | if (io_service_thread.joinable()) |
523 | 186 | io_service_thread.join(); | 224 | io_service_thread.join(); |
524 | 187 | 225 | ||
525 | 188 | /* Prepare for a potential restart */ | 226 | /* Prepare for a potential restart */ |
527 | 189 | io_service.reset(); | 227 | io_service->reset(); |
528 | 190 | } | 228 | } |
529 | 191 | 229 | ||
530 | 192 | void mf::BasicConnector::create_session_for( | 230 | void mf::BasicConnector::create_session_for( |
531 | @@ -221,11 +259,13 @@ | |||
532 | 221 | } | 259 | } |
533 | 222 | 260 | ||
534 | 223 | auto const server_socket = std::make_shared<boost::asio::local::stream_protocol::socket>( | 261 | auto const server_socket = std::make_shared<boost::asio::local::stream_protocol::socket>( |
536 | 224 | io_service, boost::asio::local::stream_protocol(), socket_fd[server]); | 262 | *io_service, |
537 | 263 | boost::asio::local::stream_protocol(), | ||
538 | 264 | socket_fd[server]); | ||
539 | 225 | 265 | ||
540 | 226 | report->creating_socket_pair(socket_fd[server], socket_fd[client]); | 266 | report->creating_socket_pair(socket_fd[server], socket_fd[client]); |
541 | 227 | 267 | ||
543 | 228 | create_session_for(server_socket, connect_handler); | 268 | create_session_for(make_socket_self_contained(io_service, server_socket), connect_handler); |
544 | 229 | 269 | ||
545 | 230 | return socket_fd[client]; | 270 | return socket_fd[client]; |
546 | 231 | } | 271 | } |
547 | 232 | 272 | ||
548 | === modified file 'src/server/frontend/published_socket_connector.h' | |||
549 | --- src/server/frontend/published_socket_connector.h 2017-07-28 17:00:43 +0000 | |||
550 | +++ src/server/frontend/published_socket_connector.h 2017-08-16 07:06:44 +0000 | |||
551 | @@ -62,7 +62,7 @@ | |||
552 | 62 | std::shared_ptr<boost::asio::local::stream_protocol::socket> const& server_socket, | 62 | std::shared_ptr<boost::asio::local::stream_protocol::socket> const& server_socket, |
553 | 63 | std::function<void(std::shared_ptr<Session> const& session)> const& connect_handler) const; | 63 | std::function<void(std::shared_ptr<Session> const& session)> const& connect_handler) const; |
554 | 64 | 64 | ||
556 | 65 | boost::asio::io_service mutable io_service; | 65 | std::shared_ptr<boost::asio::io_service> const io_service; |
557 | 66 | boost::asio::io_service::work work; | 66 | boost::asio::io_service::work work; |
558 | 67 | std::shared_ptr<ConnectorReport> const report; | 67 | std::shared_ptr<ConnectorReport> const report; |
559 | 68 | 68 | ||
560 | 69 | 69 | ||
561 | === modified file 'src/server/frontend/session_mediator.cpp' | |||
562 | --- src/server/frontend/session_mediator.cpp 2017-07-28 17:00:43 +0000 | |||
563 | +++ src/server/frontend/session_mediator.cpp 2017-08-16 07:06:44 +0000 | |||
564 | @@ -60,6 +60,7 @@ | |||
565 | 60 | #include "mir/cookie/authority.h" | 60 | #include "mir/cookie/authority.h" |
566 | 61 | #include "mir/module_properties.h" | 61 | #include "mir/module_properties.h" |
567 | 62 | #include "mir/graphics/graphic_buffer_allocator.h" | 62 | #include "mir/graphics/graphic_buffer_allocator.h" |
568 | 63 | #include "mir/executor.h" | ||
569 | 63 | 64 | ||
570 | 64 | #include "mir/geometry/rectangles.h" | 65 | #include "mir/geometry/rectangles.h" |
571 | 65 | #include "protobuf_buffer_packer.h" | 66 | #include "protobuf_buffer_packer.h" |
572 | @@ -112,7 +113,8 @@ | |||
573 | 112 | std::shared_ptr<mir::cookie::Authority> const& cookie_authority, | 113 | std::shared_ptr<mir::cookie::Authority> const& cookie_authority, |
574 | 113 | std::shared_ptr<mf::InputConfigurationChanger> const& input_changer, | 114 | std::shared_ptr<mf::InputConfigurationChanger> const& input_changer, |
575 | 114 | std::vector<mir::ExtensionDescription> const& extensions, | 115 | std::vector<mir::ExtensionDescription> const& extensions, |
577 | 115 | std::shared_ptr<mg::GraphicBufferAllocator> const& allocator) : | 116 | std::shared_ptr<mg::GraphicBufferAllocator> const& allocator, |
578 | 117 | mir::Executor& executor) : | ||
579 | 116 | client_pid_(0), | 118 | client_pid_(0), |
580 | 117 | shell(shell), | 119 | shell(shell), |
581 | 118 | ipc_operations(ipc_operations), | 120 | ipc_operations(ipc_operations), |
582 | @@ -131,7 +133,8 @@ | |||
583 | 131 | cookie_authority(cookie_authority), | 133 | cookie_authority(cookie_authority), |
584 | 132 | input_changer(input_changer), | 134 | input_changer(input_changer), |
585 | 133 | extensions(extensions), | 135 | extensions(extensions), |
587 | 134 | allocator{allocator} | 136 | allocator{allocator}, |
588 | 137 | executor{executor} | ||
589 | 135 | { | 138 | { |
590 | 136 | } | 139 | } |
591 | 137 | 140 | ||
592 | @@ -384,17 +387,21 @@ | |||
593 | 384 | public: | 387 | public: |
594 | 385 | AutoSendBuffer( | 388 | AutoSendBuffer( |
595 | 386 | std::shared_ptr<mg::Buffer> const& wrapped, | 389 | std::shared_ptr<mg::Buffer> const& wrapped, |
596 | 390 | mir::Executor& executor, | ||
597 | 387 | std::weak_ptr<mf::BufferSink> const& sink) | 391 | std::weak_ptr<mf::BufferSink> const& sink) |
598 | 388 | : buffer{wrapped}, | 392 | : buffer{wrapped}, |
599 | 393 | executor{executor}, | ||
600 | 389 | sink{sink} | 394 | sink{sink} |
601 | 390 | { | 395 | { |
602 | 391 | } | 396 | } |
603 | 392 | ~AutoSendBuffer() | 397 | ~AutoSendBuffer() |
604 | 393 | { | 398 | { |
609 | 394 | if (auto live_sink = sink.lock()) | 399 | executor.spawn( |
610 | 395 | { | 400 | [maybe_sink = sink, to_send = std::move(buffer)]() |
611 | 396 | live_sink->update_buffer(*buffer); | 401 | { |
612 | 397 | } | 402 | if (auto const live_sink = maybe_sink.lock()) |
613 | 403 | live_sink->update_buffer(*to_send); | ||
614 | 404 | }); | ||
615 | 398 | } | 405 | } |
616 | 399 | 406 | ||
617 | 400 | std::shared_ptr<mir::graphics::NativeBuffer> native_buffer_handle() const override | 407 | std::shared_ptr<mir::graphics::NativeBuffer> native_buffer_handle() const override |
618 | @@ -423,7 +430,8 @@ | |||
619 | 423 | } | 430 | } |
620 | 424 | 431 | ||
621 | 425 | private: | 432 | private: |
623 | 426 | std::shared_ptr<mg::Buffer> const buffer; | 433 | std::shared_ptr<mg::Buffer> buffer; |
624 | 434 | mir::Executor& executor; | ||
625 | 427 | std::weak_ptr<mf::BufferSink> const sink; | 435 | std::weak_ptr<mf::BufferSink> const sink; |
626 | 428 | }; | 436 | }; |
627 | 429 | 437 | ||
628 | @@ -446,7 +454,7 @@ | |||
629 | 446 | auto b = buffer_cache.at(buffer_id); | 454 | auto b = buffer_cache.at(buffer_id); |
630 | 447 | ipc_operations->unpack_buffer(request_msg, *b); | 455 | ipc_operations->unpack_buffer(request_msg, *b); |
631 | 448 | 456 | ||
633 | 449 | stream->submit_buffer(std::make_shared<AutoSendBuffer>(b, event_sink)); | 457 | stream->submit_buffer(std::make_shared<AutoSendBuffer>(b, executor, event_sink)); |
634 | 450 | 458 | ||
635 | 451 | done->Run(); | 459 | done->Run(); |
636 | 452 | } | 460 | } |
637 | @@ -520,8 +528,10 @@ | |||
638 | 520 | 528 | ||
639 | 521 | if (request->has_id()) | 529 | if (request->has_id()) |
640 | 522 | { | 530 | { |
643 | 523 | auto stream = session->get_buffer_stream(mf::BufferStreamId(request->id().value())); | 531 | auto const stream_id = mf::BufferStreamId{request->id().value()}; |
644 | 524 | stream->associate_buffer(buffer->id()); | 532 | // We don't need the stream, but we *do* need to know it exists |
645 | 533 | auto stream = session->get_buffer_stream(stream_id); | ||
646 | 534 | stream_associated_buffers.insert(std::make_pair(stream_id, buffer->id())); | ||
647 | 525 | } | 535 | } |
648 | 526 | 536 | ||
649 | 527 | // TODO: Throw if insert fails (duplicate ID)? | 537 | // TODO: Throw if insert fails (duplicate ID)? |
650 | @@ -549,25 +559,35 @@ | |||
651 | 549 | BOOST_THROW_EXCEPTION(std::logic_error("Invalid application session")); | 559 | BOOST_THROW_EXCEPTION(std::logic_error("Invalid application session")); |
652 | 550 | 560 | ||
653 | 551 | observer->session_release_buffers_called(session->name()); | 561 | observer->session_release_buffers_called(session->name()); |
654 | 562 | |||
655 | 563 | std::vector<mg::BufferID> to_release(request->buffers().size()); | ||
656 | 564 | std::transform( | ||
657 | 565 | request->buffers().begin(), | ||
658 | 566 | request->buffers().end(), | ||
659 | 567 | to_release.begin(), | ||
660 | 568 | [](auto buffer) | ||
661 | 569 | { | ||
662 | 570 | return mg::BufferID{static_cast<uint32_t>(buffer.buffer_id())}; | ||
663 | 571 | }); | ||
664 | 572 | |||
665 | 552 | if (request->has_id()) | 573 | if (request->has_id()) |
666 | 553 | { | 574 | { |
669 | 554 | auto stream_id = mf::BufferStreamId{request->id().value()}; | 575 | auto const stream_id = mf::BufferStreamId{request->id().value()}; |
670 | 555 | try | 576 | |
671 | 577 | auto const associated_range = stream_associated_buffers.equal_range(stream_id); | ||
672 | 578 | for (auto match = associated_range.first; match != associated_range.second;) | ||
673 | 556 | { | 579 | { |
676 | 557 | auto stream = session->get_buffer_stream(stream_id); | 580 | auto const current = match; |
677 | 558 | for (auto i = 0; i < request->buffers().size(); i++) | 581 | ++match; |
678 | 582 | if (std::find(to_release.begin(), to_release.end(), current->second) != to_release.end()) | ||
679 | 559 | { | 583 | { |
682 | 560 | mg::BufferID buffer_id{static_cast<uint32_t>(request->buffers(i).buffer_id())}; | 584 | |
683 | 561 | stream->disassociate_buffer(buffer_id); | 585 | stream_associated_buffers.erase(current); |
684 | 562 | } | 586 | } |
685 | 563 | } | 587 | } |
686 | 564 | catch(...) | ||
687 | 565 | { | ||
688 | 566 | } | ||
689 | 567 | } | 588 | } |
691 | 568 | for (auto i = 0; i < request->buffers().size(); i++) | 589 | for (auto const& buffer_id : to_release) |
692 | 569 | { | 590 | { |
693 | 570 | mg::BufferID buffer_id{static_cast<uint32_t>(request->buffers(i).buffer_id())}; | ||
694 | 571 | buffer_cache.erase(buffer_id); | 591 | buffer_cache.erase(buffer_id); |
695 | 572 | } | 592 | } |
696 | 573 | done->Run(); | 593 | done->Run(); |
697 | @@ -1014,6 +1034,12 @@ | |||
698 | 1014 | 1034 | ||
699 | 1015 | session->destroy_buffer_stream(id); | 1035 | session->destroy_buffer_stream(id); |
700 | 1016 | 1036 | ||
701 | 1037 | auto const associated_range = stream_associated_buffers.equal_range(id) ; | ||
702 | 1038 | for (auto match = associated_range.first; match != associated_range.second; ++match) | ||
703 | 1039 | { | ||
704 | 1040 | buffer_cache.erase(match->second); | ||
705 | 1041 | } | ||
706 | 1042 | |||
707 | 1017 | done->Run(); | 1043 | done->Run(); |
708 | 1018 | } | 1044 | } |
709 | 1019 | 1045 | ||
710 | 1020 | 1046 | ||
711 | === modified file 'src/server/frontend/session_mediator.h' | |||
712 | --- src/server/frontend/session_mediator.h 2017-07-28 17:00:43 +0000 | |||
713 | +++ src/server/frontend/session_mediator.h 2017-08-16 07:06:44 +0000 | |||
714 | @@ -40,6 +40,8 @@ | |||
715 | 40 | 40 | ||
716 | 41 | namespace mir | 41 | namespace mir |
717 | 42 | { | 42 | { |
718 | 43 | class Executor; | ||
719 | 44 | |||
720 | 43 | namespace cookie | 45 | namespace cookie |
721 | 44 | { | 46 | { |
722 | 45 | class Authority; | 47 | class Authority; |
723 | @@ -127,7 +129,8 @@ | |||
724 | 127 | std::shared_ptr<cookie::Authority> const& cookie_authority, | 129 | std::shared_ptr<cookie::Authority> const& cookie_authority, |
725 | 128 | std::shared_ptr<InputConfigurationChanger> const& input_changer, | 130 | std::shared_ptr<InputConfigurationChanger> const& input_changer, |
726 | 129 | std::vector<mir::ExtensionDescription> const& extensions, | 131 | std::vector<mir::ExtensionDescription> const& extensions, |
728 | 130 | std::shared_ptr<graphics::GraphicBufferAllocator> const& allocator); | 132 | std::shared_ptr<graphics::GraphicBufferAllocator> const& allocator, |
729 | 133 | mir::Executor& executor); | ||
730 | 131 | 134 | ||
731 | 132 | ~SessionMediator() noexcept; | 135 | ~SessionMediator() noexcept; |
732 | 133 | 136 | ||
733 | @@ -302,7 +305,9 @@ | |||
734 | 302 | std::shared_ptr<InputConfigurationChanger> const input_changer; | 305 | std::shared_ptr<InputConfigurationChanger> const input_changer; |
735 | 303 | std::vector<mir::ExtensionDescription> const extensions; | 306 | std::vector<mir::ExtensionDescription> const extensions; |
736 | 304 | std::unordered_map<graphics::BufferID, std::shared_ptr<graphics::Buffer>> buffer_cache; | 307 | std::unordered_map<graphics::BufferID, std::shared_ptr<graphics::Buffer>> buffer_cache; |
737 | 308 | std::unordered_multimap<BufferStreamId, graphics::BufferID> stream_associated_buffers; | ||
738 | 305 | std::shared_ptr<graphics::GraphicBufferAllocator> const allocator; | 309 | std::shared_ptr<graphics::GraphicBufferAllocator> const allocator; |
739 | 310 | mir::Executor& executor; | ||
740 | 306 | 311 | ||
741 | 307 | ScreencastBufferTracker screencast_buffer_tracker; | 312 | ScreencastBufferTracker screencast_buffer_tracker; |
742 | 308 | 313 | ||
743 | 309 | 314 | ||
744 | === modified file 'tests/include/mir/test/doubles/stub_buffer_stream.h' | |||
745 | --- tests/include/mir/test/doubles/stub_buffer_stream.h 2017-07-28 17:00:43 +0000 | |||
746 | +++ tests/include/mir/test/doubles/stub_buffer_stream.h 2017-08-16 07:06:44 +0000 | |||
747 | @@ -79,8 +79,6 @@ | |||
748 | 79 | void add_observer(std::shared_ptr<scene::SurfaceObserver> const&) override {} | 79 | void add_observer(std::shared_ptr<scene::SurfaceObserver> const&) override {} |
749 | 80 | void remove_observer(std::weak_ptr<scene::SurfaceObserver> const&) override {} | 80 | void remove_observer(std::weak_ptr<scene::SurfaceObserver> const&) override {} |
750 | 81 | bool has_submitted_buffer() const override { return true; } | 81 | bool has_submitted_buffer() const override { return true; } |
751 | 82 | void associate_buffer(graphics::BufferID) override {} | ||
752 | 83 | void disassociate_buffer(graphics::BufferID) override {} | ||
753 | 84 | void set_scale(float) override {} | 82 | void set_scale(float) override {} |
754 | 85 | 83 | ||
755 | 86 | std::shared_ptr<graphics::Buffer> stub_compositor_buffer; | 84 | std::shared_ptr<graphics::Buffer> stub_compositor_buffer; |
756 | 87 | 85 | ||
757 | === modified file 'tests/unit-tests/compositor/test_multi_monitor_arbiter.cpp' | |||
758 | --- tests/unit-tests/compositor/test_multi_monitor_arbiter.cpp 2017-07-28 17:00:43 +0000 | |||
759 | +++ tests/unit-tests/compositor/test_multi_monitor_arbiter.cpp 2017-08-16 07:06:44 +0000 | |||
760 | @@ -39,12 +39,6 @@ | |||
761 | 39 | { | 39 | { |
762 | 40 | throw std::runtime_error("this stub doesnt support this"); | 40 | throw std::runtime_error("this stub doesnt support this"); |
763 | 41 | } | 41 | } |
764 | 42 | std::future<void> schedule_nonblocking( | ||
765 | 43 | std::shared_ptr<mg::Buffer> const&) override | ||
766 | 44 | { | ||
767 | 45 | throw std::runtime_error("this stub doesnt support this"); | ||
768 | 46 | return {}; | ||
769 | 47 | } | ||
770 | 48 | unsigned int num_scheduled() override | 42 | unsigned int num_scheduled() override |
771 | 49 | { | 43 | { |
772 | 50 | return sched.size() - current; | 44 | return sched.size() - current; |
773 | 51 | 45 | ||
774 | === modified file 'tests/unit-tests/compositor/test_queueing_schedule.cpp' | |||
775 | --- tests/unit-tests/compositor/test_queueing_schedule.cpp 2017-07-28 17:00:43 +0000 | |||
776 | +++ tests/unit-tests/compositor/test_queueing_schedule.cpp 2017-08-16 07:06:44 +0000 | |||
777 | @@ -75,25 +75,6 @@ | |||
778 | 75 | EXPECT_FALSE(schedule.num_scheduled()); | 75 | EXPECT_FALSE(schedule.num_scheduled()); |
779 | 76 | } | 76 | } |
780 | 77 | 77 | ||
781 | 78 | TEST_F(QueueingSchedule, nonblocking_schedule_queues_buffers_up) | ||
782 | 79 | { | ||
783 | 80 | EXPECT_EQ(0u, schedule.num_scheduled()); | ||
784 | 81 | |||
785 | 82 | std::vector<std::shared_ptr<mg::Buffer>> scheduled_buffers { | ||
786 | 83 | buffers[1], buffers[3], buffers[0], buffers[2], buffers[4] | ||
787 | 84 | }; | ||
788 | 85 | |||
789 | 86 | for (auto& buffer : scheduled_buffers) | ||
790 | 87 | { | ||
791 | 88 | auto deferred_io = schedule.schedule_nonblocking(buffer); | ||
792 | 89 | EXPECT_FALSE(deferred_io.valid()); | ||
793 | 90 | } | ||
794 | 91 | |||
795 | 92 | EXPECT_EQ(scheduled_buffers.size(), schedule.num_scheduled()); | ||
796 | 93 | EXPECT_THAT(drain_queue(), ContainerEq(scheduled_buffers)); | ||
797 | 94 | EXPECT_EQ(0u, schedule.num_scheduled()); | ||
798 | 95 | } | ||
799 | 96 | |||
800 | 97 | TEST_F(QueueingSchedule, queuing_the_same_buffer_moves_it_to_front_of_queue) | 78 | TEST_F(QueueingSchedule, queuing_the_same_buffer_moves_it_to_front_of_queue) |
801 | 98 | { | 79 | { |
802 | 99 | for(auto i = 0u; i < num_buffers; i++) | 80 | for(auto i = 0u; i < num_buffers; i++) |
803 | 100 | 81 | ||
804 | === modified file 'tests/unit-tests/frontend/test_session_mediator.cpp' | |||
805 | --- tests/unit-tests/frontend/test_session_mediator.cpp 2017-07-28 17:00:43 +0000 | |||
806 | +++ tests/unit-tests/frontend/test_session_mediator.cpp 2017-08-16 07:06:44 +0000 | |||
807 | @@ -272,6 +272,22 @@ | |||
808 | 272 | std::vector<std::weak_ptr<mg::Buffer>> allocated_buffers; | 272 | std::vector<std::weak_ptr<mg::Buffer>> allocated_buffers; |
809 | 273 | }; | 273 | }; |
810 | 274 | 274 | ||
811 | 275 | class MockExecutor : public mir::Executor | ||
812 | 276 | { | ||
813 | 277 | public: | ||
814 | 278 | MockExecutor() | ||
815 | 279 | { | ||
816 | 280 | ON_CALL(*this, spawn_thunk(_)) | ||
817 | 281 | .WillByDefault(InvokeArgument<0>()); | ||
818 | 282 | } | ||
819 | 283 | |||
820 | 284 | void spawn(std::function<void()>&& task) override | ||
821 | 285 | { | ||
822 | 286 | spawn_thunk(task); | ||
823 | 287 | } | ||
824 | 288 | MOCK_METHOD1(spawn_thunk, void(std::function<void()>)); | ||
825 | 289 | }; | ||
826 | 290 | |||
827 | 275 | struct SessionMediator : public ::testing::Test | 291 | struct SessionMediator : public ::testing::Test |
828 | 276 | { | 292 | { |
829 | 277 | SessionMediator() | 293 | SessionMediator() |
830 | @@ -296,7 +312,8 @@ | |||
831 | 296 | mir::cookie::Authority::create(), | 312 | mir::cookie::Authority::create(), |
832 | 297 | mt::fake_shared(mock_input_config_changer), | 313 | mt::fake_shared(mock_input_config_changer), |
833 | 298 | {}, | 314 | {}, |
835 | 299 | allocator} | 315 | allocator, |
836 | 316 | executor} | ||
837 | 300 | { | 317 | { |
838 | 301 | using namespace ::testing; | 318 | using namespace ::testing; |
839 | 302 | 319 | ||
840 | @@ -327,7 +344,8 @@ | |||
841 | 327 | std::make_shared<mtd::NullANRDetector>(), | 344 | std::make_shared<mtd::NullANRDetector>(), |
842 | 328 | mir::cookie::Authority::create(), | 345 | mir::cookie::Authority::create(), |
843 | 329 | mt::fake_shared(mock_input_config_changer), std::vector<mir::ExtensionDescription>{}, | 346 | mt::fake_shared(mock_input_config_changer), std::vector<mir::ExtensionDescription>{}, |
845 | 330 | allocator); | 347 | allocator, |
846 | 348 | executor); | ||
847 | 331 | } | 349 | } |
848 | 332 | 350 | ||
849 | 333 | std::shared_ptr<mf::SessionMediator> create_session_mediator_with_screencast( | 351 | std::shared_ptr<mf::SessionMediator> create_session_mediator_with_screencast( |
850 | @@ -343,7 +361,8 @@ | |||
851 | 343 | std::make_shared<mtd::NullANRDetector>(), | 361 | std::make_shared<mtd::NullANRDetector>(), |
852 | 344 | mir::cookie::Authority::create(), | 362 | mir::cookie::Authority::create(), |
853 | 345 | mt::fake_shared(mock_input_config_changer), std::vector<mir::ExtensionDescription>{}, | 363 | mt::fake_shared(mock_input_config_changer), std::vector<mir::ExtensionDescription>{}, |
855 | 346 | allocator); | 364 | allocator, |
856 | 365 | executor); | ||
857 | 347 | } | 366 | } |
858 | 348 | 367 | ||
859 | 349 | std::shared_ptr<mf::SessionMediator> create_session_mediator_with_event_sink( | 368 | std::shared_ptr<mf::SessionMediator> create_session_mediator_with_event_sink( |
860 | @@ -447,7 +466,8 @@ | |||
861 | 447 | mir::cookie::Authority::create(), | 466 | mir::cookie::Authority::create(), |
862 | 448 | mt::fake_shared(mock_input_config_changer), | 467 | mt::fake_shared(mock_input_config_changer), |
863 | 449 | std::vector<mir::ExtensionDescription>{}, | 468 | std::vector<mir::ExtensionDescription>{}, |
865 | 450 | allocator); | 469 | allocator, |
866 | 470 | executor); | ||
867 | 451 | } | 471 | } |
868 | 452 | 472 | ||
869 | 453 | MockConnector connector; | 473 | MockConnector connector; |
870 | @@ -462,6 +482,7 @@ | |||
871 | 462 | std::shared_ptr<NiceMock<StubbedSession>> const stubbed_session; | 482 | std::shared_ptr<NiceMock<StubbedSession>> const stubbed_session; |
872 | 463 | std::unique_ptr<google::protobuf::Closure> null_callback; | 483 | std::unique_ptr<google::protobuf::Closure> null_callback; |
873 | 464 | std::shared_ptr<RecordingBufferAllocator> const allocator; | 484 | std::shared_ptr<RecordingBufferAllocator> const allocator; |
874 | 485 | NiceMock<MockExecutor> executor; | ||
875 | 465 | mf::SessionMediator mediator; | 486 | mf::SessionMediator mediator; |
876 | 466 | 487 | ||
877 | 467 | mp::ConnectParameters connect_parameters; | 488 | mp::ConnectParameters connect_parameters; |
878 | @@ -507,7 +528,8 @@ | |||
879 | 507 | std::make_shared<mtd::NullANRDetector>(), | 528 | std::make_shared<mtd::NullANRDetector>(), |
880 | 508 | mir::cookie::Authority::create(), | 529 | mir::cookie::Authority::create(), |
881 | 509 | mt::fake_shared(mock_input_config_changer), {}, | 530 | mt::fake_shared(mock_input_config_changer), {}, |
883 | 510 | allocator}; | 531 | allocator, |
884 | 532 | executor}; | ||
885 | 511 | 533 | ||
886 | 512 | EXPECT_THAT(connects_handled_count, Eq(0)); | 534 | EXPECT_THAT(connects_handled_count, Eq(0)); |
887 | 513 | 535 | ||
888 | @@ -997,7 +1019,8 @@ | |||
889 | 997 | std::make_shared<mtd::NullANRDetector>(), | 1019 | std::make_shared<mtd::NullANRDetector>(), |
890 | 998 | mir::cookie::Authority::create(), | 1020 | mir::cookie::Authority::create(), |
891 | 999 | mt::fake_shared(mock_input_config_changer), {}, | 1021 | mt::fake_shared(mock_input_config_changer), {}, |
893 | 1000 | allocator}; | 1022 | allocator, |
894 | 1023 | executor}; | ||
895 | 1001 | 1024 | ||
896 | 1002 | ON_CALL(*shell, create_surface( _, _, _)) | 1025 | ON_CALL(*shell, create_surface( _, _, _)) |
897 | 1003 | .WillByDefault( | 1026 | .WillByDefault( |
898 | @@ -1155,54 +1178,6 @@ | |||
899 | 1155 | 1178 | ||
900 | 1156 | } | 1179 | } |
901 | 1157 | 1180 | ||
902 | 1158 | TEST_F(SessionMediator, disassociates_buffers_from_stream_before_destroying) | ||
903 | 1159 | { | ||
904 | 1160 | mp::BufferRelease release_buffer; | ||
905 | 1161 | mp::BufferAllocation allocate_buffer; | ||
906 | 1162 | mp::Void null; | ||
907 | 1163 | |||
908 | 1164 | // Add a fake BufferStream... | ||
909 | 1165 | auto stream_id = mf::BufferStreamId{42}; | ||
910 | 1166 | auto stream = stubbed_session->create_mock_stream(stream_id); | ||
911 | 1167 | |||
912 | 1168 | // ...and allocate a buffer to it | ||
913 | 1169 | allocate_buffer.mutable_id()->set_value(42); | ||
914 | 1170 | auto buffer_props = allocate_buffer.add_buffer_requests(); | ||
915 | 1171 | buffer_props->set_buffer_usage(0); | ||
916 | 1172 | buffer_props->set_pixel_format(0); | ||
917 | 1173 | buffer_props->set_width(230); | ||
918 | 1174 | buffer_props->set_height(230); | ||
919 | 1175 | |||
920 | 1176 | mediator.connect(&connect_parameters, &connection, null_callback.get()); | ||
921 | 1177 | mediator.allocate_buffers(&allocate_buffer, &null, null_callback.get()); | ||
922 | 1178 | |||
923 | 1179 | ASSERT_THAT(allocator->allocated_buffers.size(), Eq(1)); | ||
924 | 1180 | auto allocated_buffer = allocator->allocated_buffers.front().lock(); | ||
925 | 1181 | ASSERT_THAT(allocated_buffer, NotNull()); | ||
926 | 1182 | |||
927 | 1183 | auto const buffer_id = allocated_buffer->id(); | ||
928 | 1184 | |||
929 | 1185 | // Release our share of the buffer ownership. | ||
930 | 1186 | allocated_buffer.reset(); | ||
931 | 1187 | |||
932 | 1188 | auto buffer_item = release_buffer.add_buffers(); | ||
933 | 1189 | buffer_item->set_buffer_id(buffer_id.as_value()); | ||
934 | 1190 | release_buffer.mutable_id()->set_value(stream_id.as_value()); | ||
935 | 1191 | |||
936 | 1192 | EXPECT_CALL(*stream, disassociate_buffer(buffer_id)) | ||
937 | 1193 | .WillOnce(InvokeWithoutArgs( | ||
938 | 1194 | [weak_buffer = allocator->allocated_buffers.front()]() | ||
939 | 1195 | { | ||
940 | 1196 | // The buffer should be live here! | ||
941 | 1197 | EXPECT_THAT(weak_buffer.lock(), NotNull()); | ||
942 | 1198 | })); | ||
943 | 1199 | |||
944 | 1200 | mediator.release_buffers(&release_buffer, &null, null_callback.get()); | ||
945 | 1201 | |||
946 | 1202 | // And now we expect the buffer to have been destroyed. | ||
947 | 1203 | EXPECT_THAT(allocator->allocated_buffers.front().lock(), IsNull()); | ||
948 | 1204 | } | ||
949 | 1205 | |||
950 | 1206 | TEST_F(SessionMediator, releases_buffers_of_unknown_buffer_stream_does_not_throw) | 1181 | TEST_F(SessionMediator, releases_buffers_of_unknown_buffer_stream_does_not_throw) |
951 | 1207 | { | 1182 | { |
952 | 1208 | mp::BufferStreamId stream_to_release; | 1183 | mp::BufferStreamId stream_to_release; |
953 | @@ -1368,6 +1343,48 @@ | |||
954 | 1368 | mediator->screencast_to_buffer(&screencast_request, &null, null_callback.get()); | 1343 | mediator->screencast_to_buffer(&screencast_request, &null, null_callback.get()); |
955 | 1369 | } | 1344 | } |
956 | 1370 | 1345 | ||
957 | 1346 | TEST_F(SessionMediator, buffer_releases_are_sent_from_specified_executor) | ||
958 | 1347 | { | ||
959 | 1348 | mp::BufferRelease release_buffer; | ||
960 | 1349 | mp::BufferAllocation allocate_buffer; | ||
961 | 1350 | mp::Void null; | ||
962 | 1351 | |||
963 | 1352 | // Add a fake BufferStream... | ||
964 | 1353 | auto stream_id = mf::BufferStreamId{42}; | ||
965 | 1354 | auto stream = stubbed_session->create_mock_stream(stream_id); | ||
966 | 1355 | |||
967 | 1356 | // ...and allocate a buffer to it | ||
968 | 1357 | allocate_buffer.mutable_id()->set_value(42); | ||
969 | 1358 | auto buffer_props = allocate_buffer.add_buffer_requests(); | ||
970 | 1359 | buffer_props->set_buffer_usage(0); | ||
971 | 1360 | buffer_props->set_pixel_format(0); | ||
972 | 1361 | buffer_props->set_width(230); | ||
973 | 1362 | buffer_props->set_height(230); | ||
974 | 1363 | |||
975 | 1364 | mediator.connect(&connect_parameters, &connection, null_callback.get()); | ||
976 | 1365 | mediator.allocate_buffers(&allocate_buffer, &null, null_callback.get()); | ||
977 | 1366 | |||
978 | 1367 | ASSERT_THAT(allocator->allocated_buffers.size(), Eq(1)); | ||
979 | 1368 | auto allocated_buffer = allocator->allocated_buffers.front().lock(); | ||
980 | 1369 | ASSERT_THAT(allocated_buffer, NotNull()); | ||
981 | 1370 | |||
982 | 1371 | auto const buffer_id = allocated_buffer->id(); | ||
983 | 1372 | |||
984 | 1373 | // Submit this buffer | ||
985 | 1374 | mp::BufferRequest submit_request; | ||
986 | 1375 | submit_request.mutable_id()->set_value(stream_id.as_value()); | ||
987 | 1376 | submit_request.mutable_buffer()->set_buffer_id(buffer_id.as_value()); | ||
988 | 1377 | |||
989 | 1378 | // Ensure the submitted buffer's lifetime ends immediately, and so should | ||
990 | 1379 | // get released to the client. | ||
991 | 1380 | ON_CALL(*stream, submit_buffer(_)) | ||
992 | 1381 | .WillByDefault(Invoke([](auto const &){})); | ||
993 | 1382 | // And our buffer should be released by the executor. | ||
994 | 1383 | EXPECT_CALL(executor, spawn_thunk(_)).Times(1); | ||
995 | 1384 | |||
996 | 1385 | mediator.submit_buffer(&submit_request, &null, null_callback.get()); | ||
997 | 1386 | } | ||
998 | 1387 | |||
999 | 1371 | namespace | 1388 | namespace |
1000 | 1372 | { | 1389 | { |
1001 | 1373 | void add_software_buffer_request( | 1390 | void add_software_buffer_request( |
1002 | @@ -1540,3 +1557,36 @@ | |||
1003 | 1540 | allocator->allocated_buffers, | 1557 | allocator->allocated_buffers, |
1004 | 1541 | Each(Property(&std::weak_ptr<mg::Buffer>::expired, Eq(true)))); | 1558 | Each(Property(&std::weak_ptr<mg::Buffer>::expired, Eq(true)))); |
1005 | 1542 | } | 1559 | } |
1006 | 1560 | |||
1007 | 1561 | TEST_F(SessionMediator, release_buffer_stream_releases_associated_buffers) | ||
1008 | 1562 | { | ||
1009 | 1563 | mp::BufferStreamId stream_id; | ||
1010 | 1564 | mp::BufferAllocation allocate_buffer; | ||
1011 | 1565 | mp::Void null; | ||
1012 | 1566 | |||
1013 | 1567 | // Add a fake BufferStream... | ||
1014 | 1568 | stream_id.set_value(42); | ||
1015 | 1569 | auto stream = stubbed_session->create_mock_stream(mf::BufferStreamId{stream_id.value()}); | ||
1016 | 1570 | |||
1017 | 1571 | // ...and allocate some buffers to it | ||
1018 | 1572 | *allocate_buffer.mutable_id() = stream_id; | ||
1019 | 1573 | add_software_buffer_request(allocate_buffer, 640, 480, mir_pixel_format_argb_8888); | ||
1020 | 1574 | add_software_buffer_request(allocate_buffer, 640, 480, mir_pixel_format_argb_8888); | ||
1021 | 1575 | add_software_buffer_request(allocate_buffer, 640, 480, mir_pixel_format_argb_8888); | ||
1022 | 1576 | |||
1023 | 1577 | mediator.connect(&connect_parameters, &connection, null_callback.get()); | ||
1024 | 1578 | mediator.allocate_buffers(&allocate_buffer, &null, null_callback.get()); | ||
1025 | 1579 | |||
1026 | 1580 | // All the buffers should have been allocated and should be live | ||
1027 | 1581 | ASSERT_THAT(allocator->allocated_buffers.size(), Eq(3)); | ||
1028 | 1582 | ASSERT_THAT( | ||
1029 | 1583 | allocator->allocated_buffers, | ||
1030 | 1584 | Each(Property(&std::weak_ptr<mg::Buffer>::expired, Eq(false)))); | ||
1031 | 1585 | |||
1032 | 1586 | mediator.release_buffer_stream(&stream_id, &null, null_callback.get()); | ||
1033 | 1587 | |||
1034 | 1588 | // Releasing the BufferStream should have ended the lifetime of all the buffers allocated to it. | ||
1035 | 1589 | EXPECT_THAT( | ||
1036 | 1590 | allocator->allocated_buffers, | ||
1037 | 1591 | Each(Property(&std::weak_ptr<mg::Buffer>::expired, Eq(true)))); | ||
1038 | 1592 | } |
FAILED: Continuous integration, rev:4212 /mir-jenkins. ubuntu. com/job/ mir-ci/ 3474/ /mir-jenkins. ubuntu. com/job/ build-mir/ 4747/console /mir-jenkins. ubuntu. com/job/ build-0- fetch/4905 /mir-jenkins. ubuntu. com/job/ build-1- sourcepkg/ release= artful/ 4894 /mir-jenkins. ubuntu. com/job/ build-1- sourcepkg/ release= xenial/ 4894 /mir-jenkins. ubuntu. com/job/ build-1- sourcepkg/ release= zesty/4894 /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= amd64,compiler= clang,platform= mesa,release= artful/ 4784 /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= amd64,compiler= clang,platform= mesa,release= artful/ 4784/artifact/ output/ *zip*/output. zip /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= amd64,compiler= clang,platform= mesa,release= zesty/4784 /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= amd64,compiler= clang,platform= mesa,release= zesty/4784/ artifact/ output/ *zip*/output. zip /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= amd64,compiler= gcc,platform= mesa,release= artful/ 4784 /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= amd64,compiler= gcc,platform= mesa,release= artful/ 4784/artifact/ output/ *zip*/output. zip /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= amd64,compiler= gcc,platform= mesa,release= xenial/ 4784/console /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= amd64,compiler= gcc,platform= mesa,release= zesty/4784 /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= amd64,compiler= gcc,platform= mesa,release= zesty/4784/ artifact/ output/ *zip*/output. zip /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= cross-armhf, compiler= gcc,platform= mesa,release= artful/ 4784 /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= cross-armhf, compiler= gcc,platform= mesa,release= artful/ 4784/artifact/ output/ *zip*/output. zip /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= cross-armhf, compiler= gcc,platform= mesa,release= zesty/4784 /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= cross-armhf, compiler= gcc,platform= mesa,release= zesty/4784/ artifact/ output/ *zip*/output. zip /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= i386,compiler= gcc,platform= mesa,release= xenial/ 4784/console
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
Click here to trigger a rebuild: /mir-jenkins. ubuntu. com/job/ mir-ci/ 3474/rebuild
https:/