-
Notifications
You must be signed in to change notification settings - Fork 863
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
Add a new srt_epoll_wait2 function with trigger mode available #626
Conversation
Please explain in details what you have changed in the current functionality, what the new functionality brings, and the API usage for the new API function. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add unit tests for the new functions. Check out the existing epoll tests.
A description of the changes, like @ethouris has already asked about, is also required.
…uments which are using spaces) - srt.h: small correction of the operator&(int, SRT_EPOLL_OPT) return condition, more readable
Let me enumerate some things that need fixing so that any review can be reliable:
And please also avoid assignment in conditional expressions. This doesn't make the code any better, and it worsens readability. |
- add an exception in epoll_wait2 if the fdsSet is NULL and timeout is infinite - change "trigger" to "edge" in the variable names (it is the epoll edge trigger mode, as opposed to level trigger mode)
- moved epoll_wait2 code to CUDT::epoll_wait because exception must not be thrown there
- correction of a warning in srtcore/api.cpp
You will find the unit tests in the commit 6ae572f |
- optimization of the CEPoll::uwait
- correction of the deadlock case
Merge last changes from epoll-wait2
- Modified test WaitEmptyCall2 for this case
- Modified test WaitEmptyCall2 for this case
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
API-functions.md states:
In the edge-triggered mode the function only returns socket states which have changed since last call.
So I expect the following test to succeed when the epoll state of the socket is changed from false
to true
and back. See stage 3. Or are those changes intentionally not signaled?
HandleEpollEvent2EdgeTrue2False (click to expand)
TEST(CEPoll, HandleEpollEvent2EdgeTrue2False)
{
ASSERT_EQ(srt_startup(), 0);
SRTSOCKET client_sock = srt_socket(AF_INET, SOCK_DGRAM, 0);
EXPECT_NE(client_sock, SRT_ERROR);
CEPoll epoll;
const int epoll_id = epoll.create();
ASSERT_GE(epoll_id, 0);
// 1. Add socket to epoll
const int epoll_out = SRT_EPOLL_OUT | SRT_EPOLL_ERR;
ASSERT_NE(epoll.add_usock(epoll_id, client_sock, &epoll_out), SRT_ERROR);
SRT_EPOLL_EVENT fds[1024];
int result = SRT_ERROR;
// 2. The state is not signaled, so the edge-type triggering should not succeed,
// and should return by timeout instead.
bool exception_happened = false;
try
{
result = epoll.uwait(epoll_id, fds, 1024, 150, true);
}
catch (const CUDTException &e)
{
exception_happened = true;
EXPECT_EQ(e.getErrorCode(), MJ_AGAIN * 1000 + MN_XMTIMEOUT);
}
ASSERT_EQ(exception_happened, true);
// 3. Update event on the socket.
set<int> epoll_ids = { epoll_id };
epoll.update_events(client_sock, epoll_ids, SRT_EPOLL_OUT, true);
epoll.update_events(client_sock, epoll_ids, SRT_EPOLL_OUT, false);
// 4. The state is changed, so we should catch it with wait.
result = epoll.uwait(epoll_id, fds, 1024, 150, true);
ASSERT_EQ(result, 1);
ASSERT_EQ(fds[0].events, SRT_EPOLL_OUT);
try
{
EXPECT_EQ(epoll.remove_usock(epoll_id, client_sock), 0);
}
catch (CUDTException &ex)
{
cerr << ex.getErrorMessage() << endl;
EXPECT_EQ(0, 1);
}
try
{
EXPECT_EQ(epoll.release(epoll_id), 0);
}
catch (CUDTException &ex)
{
cerr << ex.getErrorMessage() << endl;
EXPECT_EQ(0, 1);
}
EXPECT_EQ(srt_cleanup(), 0);
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The following test fails on stage 6, that does not look well together with the behavior on state 2. Please comment.
TEST: HandleEpollEvent2Edge (click to expand)
TEST(CEPoll, HandleEpollEvent2Edge)
{
ASSERT_EQ(srt_startup(), 0);
SRTSOCKET client_sock = srt_socket(AF_INET, SOCK_DGRAM, 0);
EXPECT_NE(client_sock, SRT_ERROR);
CEPoll epoll;
const int epoll_id = epoll.create();
ASSERT_GE(epoll_id, 0);
// 1. Add socket to epoll
const int epoll_out = SRT_EPOLL_OUT | SRT_EPOLL_ERR;
ASSERT_NE(epoll.add_usock(epoll_id, client_sock, &epoll_out), SRT_ERROR);
SRT_EPOLL_EVENT fds[1024];
int result = SRT_ERROR;
// 2. The state is not signaled, so the edge-type triggering should not succeed,
// and should return by timeout instead.
bool exception_happened = false;
try
{
result = epoll.uwait(epoll_id, fds, 1024, 150, true);
}
catch (const CUDTException &e)
{
exception_happened = true;
EXPECT_EQ(e.getErrorCode(), MJ_AGAIN * 1000 + MN_XMTIMEOUT);
}
ASSERT_EQ(exception_happened, true);
// 3. Update event on the socket.
set<int> epoll_ids = { epoll_id };
epoll.update_events(client_sock, epoll_ids, SRT_EPOLL_OUT, true);
// 4. The state is changed, so we should catch it with wait.
result = epoll.uwait(epoll_id, fds, 1024, 150, true);
ASSERT_EQ(result, 1);
ASSERT_EQ(fds[0].events, SRT_EPOLL_OUT);
// 5. Add the socket back to epoll, because it was deleted by edge-triggering.
ASSERT_NE(epoll.add_usock(epoll_id, client_sock, &epoll_out), SRT_ERROR);
// 6. Now the state remains signaled, so the edge-type triggering should not succeed,
// and return by timeout.
exception_happened = false;
try
{
result = epoll.uwait(epoll_id, fds, 1024, 150, true);
}
catch (const CUDTException &e)
{
exception_happened = true;
EXPECT_EQ(e.getErrorCode(), MJ_AGAIN * 1000 + MN_XMTIMEOUT);
}
ASSERT_EQ(exception_happened, true);
// Change state to false. Should trigger the edge-wait.
epoll.update_events(client_sock, epoll_ids, SRT_EPOLL_OUT, false);
result = epoll.uwait(epoll_id, fds, 1024, 150, true);
ASSERT_EQ(result, 1);
ASSERT_EQ(fds[0].events, SRT_EPOLL_OUT);
try
{
EXPECT_EQ(epoll.remove_usock(epoll_id, client_sock), 0);
}
catch (CUDTException &ex)
{
cerr << ex.getErrorMessage() << endl;
EXPECT_EQ(0, 1);
}
try
{
EXPECT_EQ(epoll.release(epoll_id), 0);
}
catch (CUDTException &ex)
{
cerr << ex.getErrorMessage() << endl;
EXPECT_EQ(0, 1);
}
EXPECT_EQ(srt_cleanup(), 0);
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok I have updated API.md in the PR #608 |
small correction of SRTO_IPV6ONLY option description, reported from PR Haivision#608
Let me ask a question: is then the "edge-triggered mode" such a mode that starts waiting in some given mode for every socket (some may be ready, some others not ready), this state is persistent between the calls, and the function exits only if a status on any of the socket is different than the previous persistent state? Example: I state sockets are not ready, so I subscribe two sockets: 10 and 11
Then I do this wait for edge, I get readiness on 10
I handle this case and enter waiting function again with this state. Then:
|
Yes exactly.
If there is no event handled (
The SRTSOCKET is returned in the SRT_EPOLL_EVENT_ here in the fd property. |
Ok, maybe different way. Do I understand it correctly that "edge triggered" only means that upon returning from the waiting function the actual ready sockets are reported in the If that's the case, your function is unfortunately not universal enough. In the branch where I'm working on redundancy, I found last time a case that I need to report a second link to be attached to the group. This is not reported by usual write-readiness on the connecting group because the group is considered connected when at least one link is active. As I need this event anyway, I introduced a new event type. The problem is that in my waiting function that is used in the receiving function for groups I need to get this event, but this one I need to be "edge triggered" (gets cleared just after at least one waiting function has reported it), whereas all connected sockets from the group I need to get the current (not triggered) readiness state. In my version of the waiting function I simply clear the event if this was this special one, whereas normal events are unchanged (can only be updated). Not the best solution, but works. Therefore for that case I need that whether this is to be edge-triggered or persistent state I need to decide during subscription, not during waiting. Moreover, if I'm not mistaken, the same rule is used in libevent and kevent systems. |
One more question to the code: in I mean: won't this code do functionally exactly the same thing, and even slightly faster?
|
int CEPoll::uwait(const int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, int64_t msTimeOut, bool edgeMode) | ||
{ | ||
int64_t entertime = CTimer::getTime(); | ||
if (!fdsSet && fdsSize) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this mean that zero size is allowed if you pass NULL fdsSet? What about negative size, stating that the type is int
? Is nonempty array officially allowed and what sense does it make?
map<int, CEPollDesc>::iterator p = m_mPolls.find(eid); | ||
if (p == m_mPolls.end()) | ||
throw CUDTException(MJ_NOTSUP, MN_EIDINVAL); | ||
if (p->second.m_sUDTSocksWait.empty() && p->second.m_sLocals.empty() && (msTimeOut < 0)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Provided that the uwait
version doesn't exit on checking readiness on system sockets, shouldn't the m_sLocals
be even required to be empty?
As I analyze this code, guys, one more idea. Instead of the
By connecting it with the need of specifying edge option in the subscription, it needs first of all to define the value type for
This way, when you have the state update into readiness, you add the socket-event pair to the list and sets its iterator to [EDIT]: It turns out that the Please tell me what you think. I'm not sure about this what I write because I may still miss something in my analysis. |
Alright, @MathieuPOUX please review when you can. I have made an alternative implementation basing on the above description: my branch dev-epoll-uwait. Differences in API:
Implementation:
It is expected also performance improvements (tho I could not fully test it yet):
The notice object has also a back pointer to its watch item. This is considered safe enough, as there cannot exist a notice object without a watch object, while there can only exist a watch object without associated notice object. The consistency is ensured through forbidding external access to the containers directly, and updating only through internal functions that guard consistency. |
|
I think the dev-epoll-uwait version is correct now, I don't know if you have to apply the patch mentioned by Max above. |
Moved to #832 |
Closed as the replacement PR has been merged. |
Add a new CEPoll::wait optimized for performance and with edge trigger mode available.
This new version of CEpoll::wait() implements both edge & level trigger modes of epoll. The edge trigger mode allow to wait only for the changes of the event states listened.
In addition it divides the events in SRT_EPOLL_IN, SRT_EPOLL_OUT and SRT_EPOLL_ERR as epoll does and now return the total number of events concerned.
Usage example :