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

Make chip-tool run on a single event loop #7824

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 19 additions & 35 deletions .github/workflows/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ jobs:
strategy:
matrix:
type: [debug, tsan]
eventloop: [eventloop_same, eventloop_separate]
env:
USE_SEPARATE_EVENTLOOP: ${{ matrix.eventloop == 'eventloop_separate' }}
USE_TSAN: ${{ matrix.type == 'tsan' }}

if: github.actor != 'restyled-io[bot]'
runs-on: ubuntu-latest
Expand Down Expand Up @@ -58,30 +62,18 @@ jobs:
uses: actions/upload-artifact@v2
if: ${{ always() }}
with:
name: bootstrap-logs-${{ matrix.type }}
name: bootstrap-logs-linux-${{ matrix.type }}-${{ matrix.eventloop }}
path: |
.environment/gn_out/.ninja_log
.environment/pigweed-venv/*.log
- name: Build all clusters app
timeout-minutes: 5
run: |
case ${{ matrix.type }} in
"debug") GN_ARGS='is_tsan=false';;
"tsan") GN_ARGS='is_tsan=true';;
*) ;;
esac

scripts/examples/gn_build_example.sh examples/all-clusters-app/linux out/debug/standalone/ chip_config_network_layer_ble=false "$GN_ARGS"
scripts/examples/gn_build_example.sh examples/all-clusters-app/linux out/debug/standalone/ chip_config_network_layer_ble=false is_tsan=${USE_TSAN}
- name: Build chip-tool
timeout-minutes: 5
run: |
case ${{ matrix.type }} in
"debug") GN_ARGS='is_tsan=false';;
"tsan") GN_ARGS='is_tsan=true';;
*) ;;
esac

scripts/examples/gn_build_example.sh examples/chip-tool out/debug/standalone/ "$GN_ARGS"
scripts/examples/gn_build_example.sh examples/chip-tool out/debug/standalone/ is_tsan=${USE_TSAN} config_use_separate_eventloop=${USE_SEPARATE_EVENTLOOP}
- name: Copy objdir
run: |
# The idea is to not upload our objdir unless builds have
Expand All @@ -96,15 +88,15 @@ jobs:
uses: actions/upload-artifact@v2
if: ${{ failure() }}
with:
name: crash-core-linux-${{ matrix.type }}
name: crash-core-linux-${{ matrix.type }}-${{ matrix.eventloop }}
path: /tmp/cores/
# Cores are big; don't hold on to them too long.
retention-days: 5
- name: Uploading objdir for debugging
uses: actions/upload-artifact@v2
if: ${{ failure() }}
with:
name: crash-objdir-linux-${{ matrix.type }}
name: crash-objdir-linux-${{ matrix.type }}-${{ matrix.eventloop }}
path: objdir-clone/
# objdirs are big; don't hold on to them too long.
retention-days: 5
Expand All @@ -115,6 +107,10 @@ jobs:
strategy:
matrix:
type: [debug, tsan]
eventloop: [eventloop_same, eventloop_separate]
env:
USE_SEPARATE_EVENTLOOP: ${{ matrix.eventloop == 'eventloop_separate' }}
USE_TSAN: ${{ matrix.type == 'tsan' }}

if: github.actor != 'restyled-io[bot]'
runs-on: macos-latest
Expand Down Expand Up @@ -147,30 +143,18 @@ jobs:
uses: actions/upload-artifact@v2
if: ${{ always() }}
with:
name: bootstrap-logs-${{ matrix.type }}
name: bootstrap-logs-darwin-${{ matrix.type }}-${{ matrix.eventloop }}
path: |
.environment/gn_out/.ninja_log
.environment/pigweed-venv/*.log
- name: Run Build Test Server
timeout-minutes: 5
run: |
case ${{ matrix.type }} in
"debug") GN_ARGS='is_tsan=false';;
"tsan") GN_ARGS='is_tsan=true';;
*) ;;
esac

scripts/examples/gn_build_example.sh examples/all-clusters-app/linux out/debug/standalone/ chip_config_network_layer_ble=false "$GN_ARGS"
scripts/examples/gn_build_example.sh examples/all-clusters-app/linux out/debug/standalone/ chip_config_network_layer_ble=false is_tsan=${USE_TSAN}
- name: Build chip-tool
timeout-minutes: 5
run: |
case ${{ matrix.type }} in
"debug") GN_ARGS='is_tsan=false';;
"tsan") GN_ARGS='is_tsan=true';;
*) ;;
esac

scripts/examples/gn_build_example.sh examples/chip-tool out/debug/standalone/ "$GN_ARGS"
scripts/examples/gn_build_example.sh examples/chip-tool out/debug/standalone/ is_tsan=${USE_TSAN} config_use_separate_eventloop=${USE_SEPARATE_EVENTLOOP}
- name: Copy objdir
run: |
# The idea is to not upload our objdir unless builds have
Expand All @@ -184,21 +168,21 @@ jobs:
uses: actions/upload-artifact@v2
if: ${{ failure() }}
with:
name: crash-core-darwin-${{ matrix.type }}
name: crash-core-darwin-${{ matrix.type }}-${{ matrix.eventloop }}
path: /cores/
# Cores are big; don't hold on to them too long.
retention-days: 5
- name: Uploading diagnostic logs
uses: actions/upload-artifact@v2
if: ${{ failure() }}
with:
name: crash-log-darwin-${{ matrix.type }}
name: crash-log-darwin-${{ matrix.type }}-${{ matrix.eventloop }}
path: ~/Library/Logs/DiagnosticReports/
- name: Uploading objdir for debugging
uses: actions/upload-artifact@v2
if: ${{ failure() }}
with:
name: crash-objdir-darwin-${{ matrix.type }}
name: crash-objdir-darwin-${{ matrix.type }}-${{ matrix.eventloop }}
path: objdir-clone/
# objdirs are big; don't hold on to them too long.
retention-days: 5
7 changes: 7 additions & 0 deletions examples/chip-tool/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ import("${chip_root}/build/chip/tools.gni")

assert(chip_build_tools)

declare_args() {
# Use a separate eventloop for CHIP tasks
config_use_separate_eventloop = true
}

executable("chip-tool") {
sources = [
"commands/clusters/ModelCommand.cpp",
Expand All @@ -35,6 +40,8 @@ executable("chip-tool") {
"main.cpp",
]

defines = [ "CONFIG_USE_SEPARATE_EVENTLOOP=${config_use_separate_eventloop}" ]

deps = [
"${chip_root}/src/controller/data_model",
"${chip_root}/src/lib",
Expand Down
46 changes: 44 additions & 2 deletions examples/chip-tool/commands/common/Command.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
*/

#include "Command.h"
#include "platform/PlatformManager.h"

#include <netdb.h>
#include <sstream>
Expand Down Expand Up @@ -378,20 +379,61 @@ const char * Command::GetAttribute(void) const

void Command::UpdateWaitForResponse(bool value)
{
#if CONFIG_USE_SEPARATE_EVENTLOOP
{
std::lock_guard<std::mutex> lk(cvWaitingForResponseMutex);
mWaitingForResponse = value;
}
cvWaitingForResponse.notify_all();
#else // CONFIG_USE_SEPARATE_EVENTLOOP
if (value == false)
{
if (mCommandExitStatus != CHIP_NO_ERROR)
{
ChipLogError(chipTool, "Run command failure: %s", chip::ErrorStr(mCommandExitStatus));
}

chip::DeviceLayer::PlatformMgr().StopEventLoopTask();
}
#endif // CONFIG_USE_SEPARATE_EVENTLOOP
}

void Command::WaitForResponse(uint16_t duration)
#if CONFIG_USE_SEPARATE_EVENTLOOP

void Command::WaitForResponse(uint16_t seconds)
{
std::chrono::seconds waitingForResponseTimeout(duration);
std::chrono::seconds waitingForResponseTimeout(seconds);
std::unique_lock<std::mutex> lk(cvWaitingForResponseMutex);
auto waitingUntil = std::chrono::system_clock::now() + waitingForResponseTimeout;
if (!cvWaitingForResponse.wait_until(lk, waitingUntil, [this]() { return !this->mWaitingForResponse; }))
{
ChipLogError(chipTool, "No response from device");
}
}

#else // CONFIG_USE_SEPARATE_EVENTLOOP

static void OnResponseTimeout(chip::System::Layer *, void *, chip::System::Error)
{
ChipLogError(chipTool, "No response from device");

chip::DeviceLayer::PlatformMgr().StopEventLoopTask();
}

CHIP_ERROR Command::ScheduleWaitForResponse(uint16_t seconds)
{
chip::System::Timer * timer = nullptr;

CHIP_ERROR err = chip::DeviceLayer::SystemLayer.NewTimer(timer);
if (err == CHIP_NO_ERROR)
{
timer->Start(seconds * 1000, OnResponseTimeout, this);
}
else
{
ChipLogError(chipTool, "Failed to allocate timer");
}
return err;
}

#endif // CONFIG_USE_SEPARATE_EVENTLOOP
15 changes: 14 additions & 1 deletion examples/chip-tool/commands/common/Command.h
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,18 @@ class Command
}

void UpdateWaitForResponse(bool value);
void WaitForResponse(uint16_t duration);

// There is a certain symmetry between the single-event-loop and
// separate-event-loop approaches. With a separate event loop, we schedule
// our work on that event loop and synchronously wait (block) waiting for a
// response. When using a single event loop, we ask for an async response
// notification and then block processing work on the event loop
// synchronously until that notification happens.
#if CONFIG_USE_SEPARATE_EVENTLOOP
void WaitForResponse(uint16_t seconds);
#else // CONFIG_USE_SEPARATE_EVENTLOOP
CHIP_ERROR ScheduleWaitForResponse(uint16_t seconds);
#endif // CONFIG_USE_SEPARATE_EVENTLOOP

protected:
ExecutionContext * GetExecContext() { return mExecContext; }
Expand All @@ -202,7 +213,9 @@ class Command
const char * mName = nullptr;
std::vector<Argument> mArgs;

#if CONFIG_USE_SEPARATE_EVENTLOOP
std::condition_variable cvWaitingForResponse;
std::mutex cvWaitingForResponseMutex;
bool mWaitingForResponse{ false };
#endif // CONFIG_USE_SEPARATE_EVENTLOOP
};
34 changes: 26 additions & 8 deletions examples/chip-tool/commands/common/Commands.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,16 +70,34 @@ int Commands::Run(NodeId localId, NodeId remoteId, int argc, char ** argv)
err = mController.Init(localId, initParams);
VerifyOrExit(err == CHIP_NO_ERROR, ChipLogError(Controller, "Init failure! Commissioner: %s", chip::ErrorStr(err)));

#if CONFIG_USE_SEPARATE_EVENTLOOP
bzbarsky-apple marked this conversation as resolved.
Show resolved Hide resolved
// ServiceEvents() calls StartEventLoopTask(), which is paired with the
// StopEventLoopTask() below.
err = mController.ServiceEvents();
VerifyOrExit(err == CHIP_NO_ERROR, ChipLogError(Controller, "Init failure! Run Loop: %s", chip::ErrorStr(err)));
#endif // CONFIG_USE_SEPARATE_EVENTLOOP

err = RunCommand(localId, remoteId, argc, argv, &command);
SuccessOrExit(err);

#if !CONFIG_USE_SEPARATE_EVENTLOOP
chip::DeviceLayer::PlatformMgr().RunEventLoop();
#endif // !CONFIG_USE_SEPARATE_EVENTLOOP

if (command)
{
err = command->GetCommandExitStatus();
}

exit:
#if CONFIG_DEVICE_LAYER
if (err != CHIP_NO_ERROR)
{
ChipLogError(chipTool, "Run command failure: %s", chip::ErrorStr(err));
}

#if CONFIG_USE_SEPARATE_EVENTLOOP
chip::DeviceLayer::PlatformMgr().StopEventLoopTask();
#endif
#endif // CONFIG_USE_SEPARATE_EVENTLOOP

if (command)
{
Expand Down Expand Up @@ -176,14 +194,14 @@ CHIP_ERROR Commands::RunCommand(NodeId localId, NodeId remoteId, int argc, char
// set the variable to true, which will cause it to block indefinitely.
//
command->UpdateWaitForResponse(true);
#if CONFIG_USE_SEPARATE_EVENTLOOP
chip::DeviceLayer::PlatformMgr().ScheduleWork(RunQueuedCommand, reinterpret_cast<intptr_t>(command));
command->WaitForResponse(command->GetWaitDurationInSeconds());
err = command->GetCommandExitStatus();
if (err != CHIP_NO_ERROR)
{
ChipLogError(chipTool, "Run command failure: %s", chip::ErrorStr(err));
ExitNow();
}
#else // CONFIG_USE_SEPARATE_EVENTLOOP
err = command->Run();
SuccessOrExit(err);
command->ScheduleWaitForResponse(command->GetWaitDurationInSeconds());
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This set of lines highlight how much a mirror image of the other these two approaches are, and capture their essence:

Either you asynchronously schedule work and synchronously wait for the response (separate event loop), or synchronously do work and asynchronously wait for the response (same event loop).

Copy link
Contributor

Choose a reason for hiding this comment

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

This set of lines highlight how much a mirror image of the other these two approaches are, and capture their essence:

Either you asynchronously schedule work and synchronously wait for the response (separate event loop), or synchronously do work and asynchronously wait for the response (same event loop).

Could you also add this as a comment in the code (maybe Command.h at the first #if)?

#endif // CONFIG_USE_SEPARATE_EVENTLOOP
}

exit:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -298,15 +298,16 @@ CHIP_ERROR GenericPlatformManagerImpl_POSIX<ImplClass>::_StopEventLoopTask()
}

exit:
pthread_mutex_destroy(&mStateLock);
pthread_cond_destroy(&mEventQueueStoppedCond);
mHasValidChipTask = false;
return System::MapErrorPOSIX(err);
}

template <class ImplClass>
CHIP_ERROR GenericPlatformManagerImpl_POSIX<ImplClass>::_Shutdown()
{
pthread_mutex_destroy(&mStateLock);
pthread_cond_destroy(&mEventQueueStoppedCond);

//
// Call up to the base class _Shutdown() to perform the actual stack de-initialization
// and clean-up
Expand Down