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

Common: Blocking Loop (extracted from Fifo.cpp) #2470

Merged
merged 3 commits into from Jun 3, 2015

Conversation

degasus
Copy link
Member

@degasus degasus commented May 28, 2015

This PR tries to extract the synchronized main loop of our fifo as shared file within Common.

The new Common Class represent a way to implement a "busy loop" which sleeps on no work. It is optimized for a high frequent pushing of new data while the loop is already running.

@degasus
Copy link
Member Author

degasus commented May 28, 2015

degasus> so from a testing point of view, PR 2470 is fine?
JMC47> yes
JMC47> no change in functionality at all

Wait();
}

bool IsRunning()

This comment was marked as off-topic.

@degasus degasus force-pushed the syncgpu branch 3 times, most recently from edb6efb to f68d06f Compare May 28, 2015 16:57
/*
* This class provides a synchronized loop.
* It's a thread-safe way to trigger a new iteration without busy loops.
* It's optimized for high-usage iterations which usually are already runing while it's triggered often.

This comment was marked as off-topic.


// Mainloop of this object.
// The payload callback is called at least as often as it's needed to match the Set() requirements.
template<class F> void Run(F&& payload)

This comment was marked as off-topic.

This comment was marked as off-topic.

This comment was marked as off-topic.

This comment was marked as off-topic.

loop.Wait();
EXPECT_EQ(signaled.load(), received.load());

for (int j = 0; j < 100; j++)

This comment was marked as off-topic.

@comex
Copy link
Contributor

comex commented May 29, 2015

Okay. I think I've convinced myself that the atomic logic is correct, so sorry for doubting you @degasus :)

Proof that Wait() does not return early:
    - Flag's operations are done with std::memory_order_seq_cst,
      so the following linear idea of time is correct:
    - once Set ensures that m_is_running is true, there must be
      moments when (a) running is clear and (b) pending is clear,
      in that order, for a subsequent Wait to return
        - m_is_running is only cleared by Run while m_is_pending
          is true, so m_is_pending must subsequently be cleared
          after that clear (which itself is after Set started, so
          the work is already reflected in the other atomics)
        - m_is_pending will not be cleared until after the /next/
          payload invocation
    - (alternately, m_running could become false, but this is
      documented to cause Wait to return)

Proof that Wait() does return:
    - The only ways for the clear-running -> payload ->
      clear-pending -> set-done-event path in Run to not
      eventually make both remain false are if:
        - m_is_running is constantly set by another Set on a
          different thread - that could cause problems in a
          multiple-producer scenario, but with Dolphin, Wait/Set
          are only called from the CPU thread - in any case, it
          will at least return once activity subsides
        - m_shutdown is set.  In this case, m_running is cleared
          before m_done_event is set, so Wait will not block.
    - Since m_done_event is always set at the end of this path,
      Wait cannot miss the timing.

Suggestions:
    - There is a 100ms timeout in Run's wait that isn't obviously
      necessary.  I guess this is needed by the GPU thread for
      some event loop nonsense, but it should probably be
      customizable in this class.
    - The names of the variables are confusing and not really
      accurate - e.g. m_is_running can be false while payload is
      running.  (I partially blame this for my confusion, but of
      course atomics are hard and I'm dumb :)
    - Do we care about the multiple-producer issue?

@degasus
Copy link
Member Author

degasus commented May 30, 2015

@comex Thanks for your huge efforts, I've tried to implement your suggestions.
My two atomic variables are now within one atomic which is controlled by an enum. I hope the new name is better.
The timeout is now optional.
I've added some more comments to describe what I'm doing.

Missing:

  • I didn't care about the multiple-producer issue. I think it's possible to solve this, but I'm not sure if it's worth.
  • I'm not sure if it's worth to add a new "state" for sleeping. So we could skip the event notification while the worker thread is in the busy loop. Do you think this is worth? I've just done it. It looks nicer than I thought it will be.

It's now a new helper function within common.
This merges two atomic<bool> into one atomic<int>.
We did move the bit from one bool to another, now we can use operator--.
@degasus
Copy link
Member Author

degasus commented May 30, 2015

JMC47> seems exactly the same

@degasus degasus force-pushed the syncgpu branch 2 times, most recently from a3cb4f1 to 9c730b0 Compare May 30, 2015 11:39
@dolphin-emu-bot
Copy link
Contributor

FifoCI detected that this change impacts graphical rendering. Here are the behavior differences detected by the system:

  • mii-channel on dx-win-nv: diff

automated-fifoci-reporter

@comex
Copy link
Contributor

comex commented Jun 3, 2015

Haven't thought about the new version as thoroughly as the last one, but it seems reasonable. Thanks!

comex added a commit that referenced this pull request Jun 3, 2015
Common: Blocking Loop (extracted from Fifo.cpp)
@comex comex merged commit 0c5aa54 into dolphin-emu:master Jun 3, 2015
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
7 participants