Skip to content

Commit 2946b42

Browse files
Update python logic for executing work in chip main loop (#28449)
* Add python main loop work method * Fix typo and restyle * Fix typo * Code review updates * Comment update * Restyle --------- Co-authored-by: Andrei Litvin <andreilitvin@google.com>
1 parent 21448e7 commit 2946b42

File tree

5 files changed

+210
-50
lines changed

5 files changed

+210
-50
lines changed

src/controller/python/BUILD.gn

+9
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import("$dir_pw_build/python.gni")
2020

2121
import("${chip_root}/build/chip/tools.gni")
2222
import("${chip_root}/src/platform/python.gni")
23+
import("${chip_root}/src/system/system.gni")
2324
import("${dir_pw_unit_test}/test.gni")
2425

2526
if (current_os == "mac") {
@@ -33,6 +34,7 @@ config("controller_wno_deprecate") {
3334
declare_args() {
3435
chip_python_version = "0.0"
3536
chip_python_package_prefix = "chip"
37+
chip_python_supports_stack_locking = chip_system_config_locking != "none"
3638
}
3739

3840
shared_library("ChipDeviceCtrl") {
@@ -77,12 +79,19 @@ shared_library("ChipDeviceCtrl") {
7779
"chip/internal/ChipThreadWork.h",
7880
"chip/internal/CommissionerImpl.cpp",
7981
"chip/logging/LoggingRedirect.cpp",
82+
"chip/native/ChipMainLoopWork.h",
8083
"chip/native/PyChipError.cpp",
8184
"chip/tracing/TracingSetup.cpp",
8285
"chip/utils/DeviceProxyUtils.cpp",
8386
]
8487
defines += [ "CHIP_CONFIG_MAX_GROUPS_PER_FABRIC=50" ]
8588
defines += [ "CHIP_CONFIG_MAX_GROUP_KEYS_PER_FABRIC=50" ]
89+
90+
if (chip_python_supports_stack_locking) {
91+
sources += [ "chip/native/ChipMainLoopWork_StackLock.cpp" ]
92+
} else {
93+
sources += [ "chip/native/ChipMainLoopWork_WorkSchedule.cpp" ]
94+
}
8695
} else {
8796
sources += [
8897
"chip/server/Options.cpp",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
*
3+
* Copyright (c) 2023 Project CHIP Authors
4+
* All rights reserved.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
#pragma once
19+
20+
#include <functional>
21+
22+
namespace chip {
23+
namespace MainLoopWork {
24+
25+
/**
26+
* Executes the given function in the CHIP main loop if one exists.
27+
*
28+
* Several implementations exist, however generally:
29+
*
30+
* - if already in the chip main loop (or main loop is not running),
31+
* `f` gets executed right away
32+
* - otherwise:
33+
* - if chip stack locking is available, `f` is executed within the lock
34+
* - if chip stack locking not available, this will schedule and WAIT
35+
* for `f` to execute
36+
*/
37+
void ExecuteInMainLoop(std::function<void()> f);
38+
39+
} // namespace MainLoopWork
40+
} // namespace chip
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
*
3+
* Copyright (c) 2023 Project CHIP Authors
4+
* All rights reserved.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
#include "ChipMainLoopWork.h"
19+
20+
#include <platform/PlatformManager.h>
21+
22+
namespace chip {
23+
namespace MainLoopWork {
24+
25+
void ExecuteInMainLoop(std::function<void()> f)
26+
{
27+
// NOTE: requires CHIP_STACK_LOCK_TRACKING_ENABLED to be available (which python builds
28+
// generally have) to ensure chip stack locks are not deadlocking, since these
29+
// functions do not know the actual state of the chip main loop.
30+
//
31+
// TODO: it may be a good assumption that python code asking for this will NOT run in
32+
// chip main loop, however we try to be generic
33+
if (chip::DeviceLayer::PlatformMgr().IsChipStackLockedByCurrentThread())
34+
{
35+
f();
36+
return;
37+
}
38+
39+
chip::DeviceLayer::StackLock lock;
40+
f();
41+
}
42+
43+
} // namespace MainLoopWork
44+
} // namespace chip
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
*
3+
* Copyright (c) 2023 Project CHIP Authors
4+
* All rights reserved.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
#include "ChipMainLoopWork.h"
19+
20+
#include <platform/CHIPDeviceLayer.h>
21+
#include <semaphore.h>
22+
23+
namespace chip {
24+
namespace MainLoopWork {
25+
namespace {
26+
27+
struct WorkData
28+
{
29+
std::function<void()> callback;
30+
sem_t done;
31+
32+
WorkData() { sem_init(&done, 0 /* shared */, 0); }
33+
~WorkData() { sem_destroy(&done); }
34+
void Post() { sem_post(&done); }
35+
void Wait() { sem_wait(&done); }
36+
};
37+
38+
void PerformWork(intptr_t arg)
39+
{
40+
WorkData * work = reinterpret_cast<WorkData *>(arg);
41+
42+
work->callback();
43+
work->Post();
44+
}
45+
46+
} // namespace
47+
48+
void ExecuteInMainLoop(std::function<void()> f)
49+
{
50+
51+
// NOTE: requires CHIP_STACK_LOCK_TRACKING_ENABLED to be available (which python builds
52+
// generally have) to ensure chip stack locks are not deadlocking, since these
53+
// functions do not know the actual state of the chip main loop.
54+
//
55+
// TODO: it may be a good assumption that python code asking for this will NOT run in
56+
// chip main loop, however we try to be generic
57+
if (chip::DeviceLayer::PlatformMgr().IsChipStackLockedByCurrentThread())
58+
{
59+
f();
60+
return;
61+
}
62+
63+
// NOTE: the code below assumes that chip main loop is running.
64+
// if it does not, this will deadlock.
65+
//
66+
// IsChipStackLockedByCurrentThread is expected to be aware of main loop
67+
// not running.
68+
WorkData workdata;
69+
workdata.callback = f;
70+
chip::DeviceLayer::PlatformMgr().ScheduleWork(PerformWork, reinterpret_cast<intptr_t>(&workdata));
71+
workdata.Wait();
72+
}
73+
74+
} // namespace MainLoopWork
75+
} // namespace chip

src/controller/python/chip/tracing/TracingSetup.cpp

+42-50
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@
1616
* limitations under the License.
1717
*/
1818

19+
#include <controller/python/chip/native/ChipMainLoopWork.h>
1920
#include <controller/python/chip/native/PyChipError.h>
20-
#include <platform/PlatformManager.h>
2121

2222
#include <tracing/json/json_tracing.h>
2323
#include <tracing/perfetto/event_storage.h>
@@ -27,17 +27,6 @@
2727
#include <tracing/registry.h>
2828

2929
namespace {
30-
31-
using chip::DeviceLayer::PlatformMgr;
32-
33-
class ScopedStackLock
34-
{
35-
public:
36-
ScopedStackLock() { PlatformMgr().LockChipStack(); }
37-
38-
~ScopedStackLock() { PlatformMgr().UnlockChipStack(); }
39-
};
40-
4130
chip::Tracing::Json::JsonBackend gJsonBackend;
4231

4332
chip::Tracing::Perfetto::FileTraceOutput gPerfettoFileOutput;
@@ -47,58 +36,61 @@ chip::Tracing::Perfetto::PerfettoBackend gPerfettoBackend;
4736

4837
extern "C" void pychip_tracing_start_json_log(const char * file_name)
4938
{
50-
51-
ScopedStackLock lock;
52-
53-
gJsonBackend.CloseFile(); // just in case, ensure no file output
54-
chip::Tracing::Register(gJsonBackend);
39+
chip::MainLoopWork::ExecuteInMainLoop([] {
40+
gJsonBackend.CloseFile(); // just in case, ensure no file output
41+
chip::Tracing::Register(gJsonBackend);
42+
});
5543
}
5644

5745
extern "C" PyChipError pychip_tracing_start_json_file(const char * file_name)
5846
{
59-
ScopedStackLock lock;
60-
61-
CHIP_ERROR err = gJsonBackend.OpenFile(file_name);
62-
if (err != CHIP_NO_ERROR)
63-
{
64-
return ToPyChipError(err);
65-
}
66-
chip::Tracing::Register(gJsonBackend);
67-
return ToPyChipError(CHIP_NO_ERROR);
47+
CHIP_ERROR err = CHIP_NO_ERROR;
48+
49+
chip::MainLoopWork::ExecuteInMainLoop([&err, file_name] {
50+
err = gJsonBackend.OpenFile(file_name);
51+
if (err != CHIP_NO_ERROR)
52+
{
53+
return;
54+
}
55+
chip::Tracing::Register(gJsonBackend);
56+
});
57+
58+
return ToPyChipError(err);
6859
}
6960

7061
extern "C" void pychip_tracing_start_perfetto_system()
7162
{
72-
ScopedStackLock lock;
73-
74-
chip::Tracing::Perfetto::Initialize(perfetto::kSystemBackend);
75-
chip::Tracing::Perfetto::RegisterEventTrackingStorage();
76-
chip::Tracing::Register(gPerfettoBackend);
63+
chip::MainLoopWork::ExecuteInMainLoop([] {
64+
chip::Tracing::Perfetto::Initialize(perfetto::kSystemBackend);
65+
chip::Tracing::Perfetto::RegisterEventTrackingStorage();
66+
chip::Tracing::Register(gPerfettoBackend);
67+
});
7768
}
7869

7970
extern "C" PyChipError pychip_tracing_start_perfetto_file(const char * file_name)
8071
{
81-
ScopedStackLock lock;
82-
83-
chip::Tracing::Perfetto::Initialize(perfetto::kInProcessBackend);
84-
chip::Tracing::Perfetto::RegisterEventTrackingStorage();
85-
86-
CHIP_ERROR err = gPerfettoFileOutput.Open(file_name);
87-
if (err != CHIP_NO_ERROR)
88-
{
89-
return ToPyChipError(err);
90-
}
91-
chip::Tracing::Register(gPerfettoBackend);
92-
93-
return ToPyChipError(CHIP_NO_ERROR);
72+
CHIP_ERROR err = CHIP_NO_ERROR;
73+
chip::MainLoopWork::ExecuteInMainLoop([&err, file_name] {
74+
chip::Tracing::Perfetto::Initialize(perfetto::kInProcessBackend);
75+
chip::Tracing::Perfetto::RegisterEventTrackingStorage();
76+
77+
err = gPerfettoFileOutput.Open(file_name);
78+
if (err != CHIP_NO_ERROR)
79+
{
80+
return;
81+
}
82+
chip::Tracing::Register(gPerfettoBackend);
83+
});
84+
85+
return ToPyChipError(err);
9486
}
9587

9688
extern "C" void pychip_tracing_stop()
9789
{
98-
ScopedStackLock lock;
99-
100-
chip::Tracing::Perfetto::FlushEventTrackingStorage();
101-
gPerfettoFileOutput.Close();
102-
chip::Tracing::Unregister(gPerfettoBackend);
103-
chip::Tracing::Unregister(gJsonBackend);
90+
chip::MainLoopWork::ExecuteInMainLoop([] {
91+
chip::Tracing::Perfetto::FlushEventTrackingStorage();
92+
gPerfettoFileOutput.Close();
93+
chip::Tracing::Unregister(gPerfettoBackend);
94+
chip::Tracing::Unregister(gJsonBackend);
95+
});
10496
}

0 commit comments

Comments
 (0)