Skip to content

Introduce CFRunLoop based iomgr#22423

Merged
muxi merged 1 commit intogrpc:masterfrom
muxi:objc-poller
Apr 15, 2020
Merged

Introduce CFRunLoop based iomgr#22423
muxi merged 1 commit intogrpc:masterfrom
muxi:objc-poller

Conversation

@muxi
Copy link
Copy Markdown
Contributor

@muxi muxi commented Mar 19, 2020

Use a CFRunLoop based iomgr for CFStream on apple platforms. Workaround for the Apple bug where CFStream gets stuck on read.

The new iomgr spawns a global thread to handle all the CFStream callbacks. Network events are driven by the global thread. The pollset is a dummy conditional variable blocked on pollset_work and can be awaken by pollset_kick.

The CFStream networking layer is supposed to be included on iOS-targeted builds but not MacOS-targeted builds. It is supposed to be specified in the header port_platform.h. Specifying it for Mac in .bzl file is not the right thing to do. Fixing this issue in the PR as well.

@muxi muxi added lang/ObjC release notes: yes Indicates if PR needs to be in release notes labels Mar 19, 2020
@muxi
Copy link
Copy Markdown
Contributor Author

muxi commented Mar 20, 2020

Not ready for review yet...

@muxi
Copy link
Copy Markdown
Contributor Author

muxi commented Apr 2, 2020

@guantaol ping

Copy link
Copy Markdown
Contributor

@guantaol guantaol left a comment

Choose a reason for hiding this comment

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

General comment: please add some comments for the changes in those .cc files and .h files. I do not think I can understand what you want to achieve without comments.

grpc_core::CondVar cv;
};

static GlobalRunloopContext global_runloop_context;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

#endif // NDEBUG

struct GlobalRunloopContext {
grpc_core::Mutex mu;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Which fields are guarded by this mutex?

GrpcApplePollset* apple_pollset =
reinterpret_cast<GrpcApplePollset*>(pollset);
if (worker != nullptr) {
*worker = nullptr;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

If you set worker to nullptr, how could you kick it later?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I think it works if pollset_kick broadcasts, according to comments here

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

What happens if there is a thread that do not want to be kicked by the broadcast? Previously this thread could simply set grpc_pollset_worker** worker == nullptr. Now all the thread using the same pollset will be waken up altogether.

@muxi
Copy link
Copy Markdown
Contributor Author

muxi commented Apr 7, 2020

@guantaol - updated. PTAL

@muxi
Copy link
Copy Markdown
Contributor Author

muxi commented Apr 9, 2020

@guantaol ping~

Copy link
Copy Markdown
Contributor

@guantaol guantaol left a comment

Choose a reason for hiding this comment

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

I still need at least one other pass for this PR. Feel free to schedule a meeting to discuss this PR if that is more efficient.

@@ -0,0 +1,252 @@
/*
*
* Copyright 2015 gRPC authors.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copyright 2020

grpc_core::CondVar init_cv;
grpc_core::CondVar input_source_cv;

// Protects all the variables below
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This comment may not be true in the future if someone else adds a new member that is not protected by mu. May be better to use this style of comments (as other reviewers suggested to me in the past):

bool input_source_registered = false; /* GUARDED_BY(mu) */
...

When someday we actually introduce the GUARDED_BY annotation in grpc, it can be easily converted.

static grpc_core::Thread* gGlobalRunLoopThread = nullptr;

/// Register the stream with the dispatch queue. Callbacks of the stream will be
/// issued to the dispatch queue and managed by Grand Central Dispatch.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

When will the callback be issued, before or after read happens?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Added comment to explain in the code

}

/// Register the stream with the dispatch queue. Callbacks of the stream will be
/// issued to the dispatch queue and managed by Grand Central Dispatch.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The same question here

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Added comment to explain in the code

GrpcApplePollset* apple_pollset =
reinterpret_cast<GrpcApplePollset*>(pollset);
if (worker != nullptr) {
*worker = nullptr;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

What happens if there is a thread that do not want to be kicked by the broadcast? Previously this thread could simply set grpc_pollset_worker** worker == nullptr. Now all the thread using the same pollset will be waken up altogether.

*worker = nullptr;
}
while (!apple_pollset->kicked) {
if (apple_pollset->cv.Wait(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why don't we need to grab the pollset mutex beforehand? If it is required to hold the mutex before calling pollset_work, this should be commented out.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It is exactly because it is required to hold the mutex before calling grpc_pollset_work. I don't really understand what you mean by "this should be commented out"

@muxi
Copy link
Copy Markdown
Contributor Author

muxi commented Apr 13, 2020

@guantaol - Updated. PTAL

*worker = reinterpret_cast<grpc_pollset_worker*>(&actual_worker);
}

if (apple_pollset->outstanding_kick) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

If this is the same as the kicked_without_poller in ev_epollex_linux, I suggest using the same name for easy understanding.


return GRPC_ERROR_NONE;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Please add a comment to explain which mutex must be held before calling kick_worker().

@muxi
Copy link
Copy Markdown
Contributor Author

muxi commented Apr 15, 2020

Squashing commits

@muxi muxi merged commit 7f36ff0 into grpc:master Apr 15, 2020
@muxi muxi deleted the objc-poller branch April 15, 2020 21:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

lang/core release notes: yes Indicates if PR needs to be in release notes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants