Line data Source code
1 : /**
2 : * Copyright (c) 2025 Huawei Technologies Co., Ltd.
3 : * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4 : * CANN Open Software License Agreement Version 2.0 (the "License").
5 : * Please refer to the License for details. You may not use this file except in compliance with the License.
6 : * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7 : * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8 : * See LICENSE in the root of the software repository for the full text of the License.
9 : */
10 :
11 : #include <unordered_map>
12 : #include "acl_rt_impl.h"
13 :
14 : #include "runtime/stream.h"
15 : #include "runtime/rts/rts_stream.h"
16 : #include "runtime/rt_inner_stream.h"
17 :
18 : #include "common/log_inner.h"
19 : #include "common/error_codes_inner.h"
20 : #include "common/prof_reporter.h"
21 : #include "common/resource_statistics.h"
22 : #include "utils/data_type_utils.h"
23 :
24 : namespace {
25 : std::unordered_map<rtError_t, const char*> succStmSyncErrCodes = {
26 : {ACL_ERROR_RT_END_OF_SEQUENCE, "end of sequence"},
27 : {ACL_ERROR_RT_MODEL_ABORT_NORMAL, "model abort normal"},
28 : {ACL_ERROR_RT_AICORE_OVER_FLOW, "aicore overflow"},
29 : {ACL_ERROR_RT_AIVEC_OVER_FLOW, "aivec overflow"},
30 : {ACL_ERROR_RT_OVER_FLOW, "overflow"},
31 : {ACL_ERROR_RT_SOCKET_CLOSE, "socket close"}};
32 : }
33 :
34 : #ifdef __cplusplus
35 : extern "C" {
36 : #endif
37 :
38 : aclError aclrtCreateStreamImpl(aclrtStream* stream)
39 : {
40 : ACL_PROFILING_REG(acl::AclProfType::AclrtCreateStream);
41 : ACL_ADD_APPLY_TOTAL_COUNT(acl::ACL_STATISTICS_CREATE_DESTROY_STREAM);
42 : ACL_LOG_INFO("start to execute aclrtCreateStream");
43 : ACL_REQUIRES_NOT_NULL_WITH_INPUT_REPORT(stream);
44 :
45 : rtStream_t rtStream = nullptr;
46 : ACL_REQUIRES_RTS_OK(rtStreamCreate(&rtStream, static_cast<int32_t>(RT_STREAM_PRIORITY_DEFAULT)));
47 :
48 : *stream = static_cast<aclrtStream>(rtStream);
49 : ACL_ADD_APPLY_SUCCESS_COUNT(acl::ACL_STATISTICS_CREATE_DESTROY_STREAM);
50 : return ACL_SUCCESS;
51 : }
52 :
53 : aclError aclrtCreateStreamWithConfigImpl(aclrtStream* stream, uint32_t priority, uint32_t flag)
54 : {
55 : ACL_PROFILING_REG(acl::AclProfType::AclrtCreateStreamWithConfig);
56 : ACL_ADD_APPLY_TOTAL_COUNT(acl::ACL_STATISTICS_CREATE_DESTROY_STREAM);
57 : ACL_LOG_INFO("start to execute aclrtCreateStreamWithConfig with priority:%u, flag:%u", priority, flag);
58 : ACL_REQUIRES_NOT_NULL_WITH_INPUT_REPORT(stream);
59 :
60 : uint32_t streamFlag = 0U;
61 : if ((flag & ACL_STREAM_FAST_LAUNCH) != 0U) {
62 : streamFlag |= RT_STREAM_FAST_LAUNCH;
63 : }
64 : if ((flag & ACL_STREAM_FAST_SYNC) != 0U) {
65 : streamFlag |= RT_STREAM_FAST_SYNC;
66 : }
67 : if ((flag & ACL_STREAM_PERSISTENT) != 0U) {
68 : streamFlag |= RT_STREAM_PERSISTENT;
69 : }
70 : if ((flag & ACL_STREAM_HUGE) != 0U) {
71 : streamFlag |= RT_STREAM_HUGE;
72 : }
73 : if ((flag & ACL_STREAM_CPU_SCHEDULE) != 0U) {
74 : streamFlag |= RT_STREAM_CPU_SCHEDULE;
75 : }
76 : if ((flag & ACL_STREAM_DEVICE_USE_ONLY) != 0U) {
77 : streamFlag |= RT_STREAM_CP_PROCESS_USE;
78 : }
79 :
80 : rtStream_t rtStream = nullptr;
81 : constexpr size_t numAttrs = 2;
82 : rtStreamCreateAttr_t attrs[numAttrs];
83 : attrs[0].id = RT_STREAM_CREATE_ATTR_PRIORITY;
84 : attrs[0].value.priority = priority;
85 : attrs[1].id = RT_STREAM_CREATE_ATTR_FLAGS;
86 : attrs[1].value.flags = streamFlag;
87 : rtStreamCreateConfig_t config = {attrs, numAttrs};
88 : ACL_REQUIRES_RTS_OK(rtsStreamCreate(&rtStream, &config));
89 :
90 : *stream = static_cast<aclrtStream>(rtStream);
91 : ACL_ADD_APPLY_SUCCESS_COUNT(acl::ACL_STATISTICS_CREATE_DESTROY_STREAM);
92 : return ACL_SUCCESS;
93 : }
94 :
95 : aclError aclrtDestroyStreamImpl(aclrtStream stream)
96 : {
97 : ACL_PROFILING_REG(acl::AclProfType::AclrtDestroyStream);
98 : ACL_ADD_RELEASE_TOTAL_COUNT(acl::ACL_STATISTICS_CREATE_DESTROY_STREAM);
99 : ACL_LOG_INFO("start to execute aclrtDestroyStream");
100 : ACL_REQUIRES_NOT_NULL_WITH_INPUT_REPORT(stream);
101 :
102 : ACL_REQUIRES_RTS_OK(rtStreamDestroy(static_cast<rtStream_t>(stream)));
103 : ACL_LOG_INFO("aclrtDestroyStream success");
104 : ACL_ADD_RELEASE_SUCCESS_COUNT(acl::ACL_STATISTICS_CREATE_DESTROY_STREAM);
105 : return ACL_SUCCESS;
106 : }
107 :
108 : aclError aclrtDestroyStreamForceImpl(aclrtStream stream)
109 : {
110 : ACL_PROFILING_REG(acl::AclProfType::AclrtDestroyStreamForce);
111 : ACL_ADD_RELEASE_TOTAL_COUNT(acl::ACL_STATISTICS_CREATE_DESTROY_STREAM);
112 : ACL_LOG_INFO("start to execute aclrtDestroyStreamForce");
113 : ACL_REQUIRES_NOT_NULL_WITH_INPUT_REPORT(stream);
114 :
115 : ACL_REQUIRES_RTS_OK(rtStreamDestroyForce(static_cast<rtStream_t>(stream)));
116 : ACL_LOG_INFO("aclrtDestroyStreamForce success");
117 : ACL_ADD_RELEASE_SUCCESS_COUNT(acl::ACL_STATISTICS_CREATE_DESTROY_STREAM);
118 : return ACL_SUCCESS;
119 : }
120 :
121 : aclError aclrtSynchronizeStreamImpl(aclrtStream stream)
122 : {
123 : ACL_PROFILING_REG(acl::AclProfType::AclrtSynchronizeStream);
124 :
125 : const rtError_t rtErr = rtStreamSynchronize(static_cast<rtStream_t>(stream));
126 : if (rtErr != RT_ERROR_NONE) {
127 : const auto it = succStmSyncErrCodes.find(rtErr);
128 : if (it != succStmSyncErrCodes.cend()) {
129 : ACL_LOG_INFO("Synchronize stream success, err = %d, desc = %s", static_cast<int32_t>(rtErr), it->second);
130 : }
131 : return ACL_GET_ERRCODE_RTS(rtErr);
132 : }
133 :
134 : return ACL_SUCCESS;
135 : }
136 :
137 : aclError aclrtSynchronizeStreamWithTimeoutImpl(aclrtStream stream, int32_t timeout)
138 : {
139 : ACL_PROFILING_REG(acl::AclProfType::AclrtSynchronizeStreamWithTimeout);
140 : constexpr int32_t defaultTimeout = -1;
141 : if (timeout < defaultTimeout) {
142 : ACL_LOG_CALL_ERROR("the timeout of synchronize stream is invalid");
143 : const std::string timeoutVal = std::to_string(timeout);
144 : std::string funcName = acl::AclErrorLogManager::GetFuncNameWithoutImplSuffix(__func__);
145 : acl::AclErrorLogManager::ReportInputError(
146 : acl::INVALID_VALUE_MSG, std::vector<const char*>({"func", "value", "param", "expect"}),
147 : std::vector<const char*>({funcName.c_str(), timeoutVal.c_str(), "timeout", "[-1, INT_MAX]"}));
148 : return ACL_ERROR_RT_PARAM_INVALID;
149 : }
150 : const rtError_t rtErr = rtStreamSynchronizeWithTimeout(static_cast<rtStream_t>(stream), timeout);
151 : if (rtErr == ACL_ERROR_RT_STREAM_SYNC_TIMEOUT) {
152 : return ACL_ERROR_RT_STREAM_SYNC_TIMEOUT;
153 : }
154 : if (rtErr != RT_ERROR_NONE) {
155 : const auto it = succStmSyncErrCodes.find(rtErr);
156 : if (it != succStmSyncErrCodes.cend()) {
157 : ACL_LOG_INFO(
158 : "synchronize stream with timeout success, err = %d, desc = %s", static_cast<int32_t>(rtErr),
159 : it->second);
160 : }
161 : return ACL_GET_ERRCODE_RTS(rtErr);
162 : }
163 :
164 : return ACL_SUCCESS;
165 : }
166 :
167 : aclError aclrtStreamQueryImpl(aclrtStream stream, aclrtStreamStatus* status)
168 : {
169 : ACL_PROFILING_REG(acl::AclProfType::AclrtStreamQuery);
170 : ACL_REQUIRES_NOT_NULL_WITH_INPUT_REPORT(status);
171 :
172 : const rtError_t rtErr = rtStreamQuery(static_cast<rtStream_t>(stream));
173 : if (rtErr == RT_ERROR_NONE) {
174 : *status = ACL_STREAM_STATUS_COMPLETE;
175 : } else if (rtErr == ACL_ERROR_RT_STREAM_NOT_COMPLETE) {
176 : *status = ACL_STREAM_STATUS_NOT_READY;
177 : } else {
178 : return ACL_GET_ERRCODE_RTS(rtErr);
179 : }
180 :
181 : return ACL_SUCCESS;
182 : }
183 :
184 : aclError aclrtStreamGetPriorityImpl(aclrtStream stream, uint32_t* priority)
185 : {
186 : ACL_PROFILING_REG(acl::AclProfType::AclrtStreamGetPriority);
187 : ACL_LOG_INFO("start to execute aclrtStreamGetPriority");
188 : ACL_REQUIRES_NOT_NULL_WITH_INPUT_REPORT(priority);
189 : rtStream_t rtStream = static_cast<rtStream_t>(stream);
190 : uint32_t prio = 0U;
191 : ACL_REQUIRES_RTS_OK(rtStreamGetPriority(rtStream, &prio));
192 : *priority = prio;
193 : ACL_LOG_INFO("successfully execute aclrtStreamGetPriority, priority is %u", *priority);
194 : return ACL_SUCCESS;
195 : }
196 :
197 : aclError aclrtStreamGetFlagsImpl(aclrtStream stream, uint32_t* flags)
198 : {
199 : ACL_PROFILING_REG(acl::AclProfType::AclrtStreamGetFlags);
200 : ACL_LOG_INFO("start to execute aclrtStreamGetFlags");
201 : ACL_REQUIRES_NOT_NULL_WITH_INPUT_REPORT(flags);
202 : rtStream_t rtStream = static_cast<rtStream_t>(stream);
203 : uint32_t rtFlags = 0U;
204 : ACL_REQUIRES_RTS_OK(rtStreamGetFlags(rtStream, &rtFlags));
205 : uint32_t aclFlags = 0U;
206 : if ((rtFlags & RT_STREAM_FAST_LAUNCH) != 0U) {
207 : aclFlags |= ACL_STREAM_FAST_LAUNCH;
208 : }
209 : if ((rtFlags & RT_STREAM_FAST_SYNC) != 0U) {
210 : aclFlags |= ACL_STREAM_FAST_SYNC;
211 : }
212 : if ((rtFlags & RT_STREAM_PERSISTENT) != 0U) {
213 : aclFlags |= ACL_STREAM_PERSISTENT;
214 : }
215 : if ((rtFlags & RT_STREAM_HUGE) != 0U) {
216 : aclFlags |= ACL_STREAM_HUGE;
217 : }
218 : if ((rtFlags & RT_STREAM_CPU_SCHEDULE) != 0U) {
219 : aclFlags |= ACL_STREAM_CPU_SCHEDULE;
220 : }
221 : if ((rtFlags & RT_STREAM_CP_PROCESS_USE) != 0U) {
222 : aclFlags |= ACL_STREAM_DEVICE_USE_ONLY;
223 : }
224 : *flags = aclFlags;
225 : ACL_LOG_INFO("successfully execute aclrtStreamGetFlags, rtFlags is %#x, aclFlags is %#x", rtFlags, *flags);
226 : return ACL_SUCCESS;
227 : }
228 :
229 : aclError aclrtStreamWaitEventImpl(aclrtStream stream, aclrtEvent event)
230 : {
231 : ACL_PROFILING_REG(acl::AclProfType::AclrtStreamWaitEvent);
232 : ACL_REQUIRES_NOT_NULL_WITH_INPUT_REPORT(event);
233 :
234 : ACL_REQUIRES_RTS_OK(rtStreamWaitEvent(static_cast<rtStream_t>(stream), static_cast<rtEvent_t>(event)));
235 :
236 : return ACL_SUCCESS;
237 : }
238 :
239 : aclError aclrtStreamWaitEventWithFlagImpl(aclrtStream stream, aclrtEvent event, uint32_t timeout, uint32_t flag)
240 : {
241 : ACL_PROFILING_REG(acl::AclProfType::AclrtStreamWaitEventWithFlag);
242 : ACL_LOG_INFO("start to execute aclrtStreamWaitEventWithFlag");
243 : ACL_REQUIRES_NOT_NULL_WITH_INPUT_REPORT(event);
244 : ACL_CHECK_INVALID_VALUE_WITH_EXPECT_RET(
245 : (flag == ACL_EVENT_WAIT_DEFAULT) || (flag == ACL_EVENT_WAIT_EXTERNAL), flag,
246 : "ACL_EVENT_WAIT_DEFAULT or ACL_EVENT_WAIT_EXTERNAL", ACL_ERROR_INVALID_PARAM);
247 :
248 : if (flag == ACL_EVENT_WAIT_EXTERNAL) {
249 : ACL_CHECK_INVALID_VALUE_WITH_EXPECT_RET(
250 : timeout == 0U, timeout, "0 when flag is ACL_EVENT_WAIT_EXTERNAL", ACL_ERROR_INVALID_PARAM);
251 : }
252 :
253 : ACL_REQUIRES_RTS_OK(
254 : rtStreamWaitEventWithFlag(static_cast<rtStream_t>(stream), static_cast<rtEvent_t>(event), timeout, flag));
255 : return ACL_SUCCESS;
256 : }
257 :
258 : aclError aclrtSetStreamFailureModeImpl(aclrtStream stream, uint64_t mode)
259 : {
260 : ACL_PROFILING_REG(acl::AclProfType::AclrtSetStreamFailureMode);
261 : ACL_LOG_INFO("start to execute aclrtSetStreamFailureMode, mode is %lu", mode);
262 : ACL_REQUIRES_RTS_OK(rtStreamSetMode(static_cast<rtStream_t>(stream), mode));
263 : ACL_LOG_INFO("successfully execute aclrtSetStreamFailureMode, mode is %lu", mode);
264 : return ACL_SUCCESS;
265 : }
266 :
267 : aclError aclrtGetStreamOverflowSwitchImpl(aclrtStream stream, uint32_t* flag)
268 : {
269 : ACL_LOG_INFO("start to execute aclrtGetStreamOverflowSwitch");
270 : ACL_REQUIRES_NOT_NULL_WITH_INPUT_REPORT(flag);
271 : ACL_REQUIRES_RTS_OK(rtGetStreamOverflowSwitch(static_cast<rtStream_t>(stream), flag));
272 : ACL_LOG_INFO("successfully execute aclrtGetStreamOverflowSwitch, flag is %d.", *flag);
273 : return ACL_SUCCESS;
274 : }
275 :
276 : aclError aclrtSetStreamOverflowSwitchImpl(aclrtStream stream, uint32_t flag)
277 : {
278 : ACL_LOG_INFO("start to execute aclrtSetStreamOverflowSwitch, flag is %u.", flag);
279 : ACL_CHECK_INVALID_VALUE_WITH_EXPECT((flag == 0U) || (flag == 1U), flag, "0 or 1");
280 : ACL_REQUIRES_RTS_OK(rtSetStreamOverflowSwitch(static_cast<rtStream_t>(stream), flag));
281 : ACL_LOG_INFO("successfully execute rtSetStreamOverflowSwitch, flag is %u.", flag);
282 : return ACL_SUCCESS;
283 : }
284 :
285 : aclError aclrtStreamAbortImpl(aclrtStream stream)
286 : {
287 : ACL_PROFILING_REG(acl::AclProfType::AclrtStreamAbort);
288 : ACL_LOG_INFO("start to execute aclrtStreamAbort");
289 : ACL_REQUIRES_RTS_OK(rtStreamAbort(stream));
290 :
291 : ACL_LOG_INFO("successfully execute aclrtStreamAbort");
292 : return ACL_SUCCESS;
293 : }
294 :
295 : aclError aclrtStreamGetIdImpl(aclrtStream stream, int32_t* streamId)
296 : {
297 : ACL_PROFILING_REG(acl::AclProfType::AclrtStreamGetId);
298 : ACL_REQUIRES_NOT_NULL_WITH_INPUT_REPORT(streamId);
299 : ACL_REQUIRES_RTS_OK(rtsStreamGetId(static_cast<rtStream_t>(stream), streamId));
300 : return ACL_SUCCESS;
301 : }
302 :
303 : aclError aclrtGetStreamAvailableNumImpl(uint32_t* streamCount)
304 : {
305 : ACL_PROFILING_REG(acl::AclProfType::AclrtGetStreamAvailableNum);
306 : ACL_LOG_INFO("start to execute aclrtGetStreamAvailableNum");
307 : ACL_REQUIRES_NOT_NULL_WITH_INPUT_REPORT(streamCount);
308 :
309 : ACL_REQUIRES_RTS_OK(rtsStreamGetAvailableNum(streamCount));
310 :
311 : ACL_LOG_INFO("successfully execute aclrtGetStreamAvailableNum");
312 : return ACL_SUCCESS;
313 : }
314 :
315 : aclError aclrtSetStreamAttributeImpl(aclrtStream stream, aclrtStreamAttr stmAttrType, aclrtStreamAttrValue* value)
316 : {
317 : ACL_PROFILING_REG(acl::AclProfType::AclrtSetStreamAttribute);
318 4 : ACL_LOG_INFO("start to execute aclrtSetStreamAttribute, stmAttrType = [%s]", acl::GetStreamAttrDesc(stmAttrType));
319 : ACL_REQUIRES_NOT_NULL_WITH_INPUT_REPORT(value);
320 :
321 : ACL_REQUIRES_RTS_OK(rtsStreamSetAttribute(
322 : static_cast<rtStream_t>(stream), static_cast<rtStreamAttr>(stmAttrType),
323 : reinterpret_cast<rtStreamAttrValue_t*>(value)));
324 :
325 : ACL_LOG_INFO("successfully execute aclrtSetStreamAttribute");
326 : return ACL_SUCCESS;
327 : }
328 :
329 : aclError aclrtGetStreamAttributeImpl(aclrtStream stream, aclrtStreamAttr stmAttrType, aclrtStreamAttrValue* value)
330 : {
331 : ACL_PROFILING_REG(acl::AclProfType::AclrtGetStreamAttribute);
332 : ACL_REQUIRES_NOT_NULL_WITH_INPUT_REPORT(value);
333 :
334 : ACL_REQUIRES_RTS_OK(rtsStreamGetAttribute(
335 : static_cast<rtStream_t>(stream), static_cast<rtStreamAttr>(stmAttrType),
336 : reinterpret_cast<rtStreamAttrValue_t*>(value)));
337 :
338 : return ACL_SUCCESS;
339 : }
340 :
341 : aclError aclrtActiveStreamImpl(aclrtStream activeStream, aclrtStream stream)
342 : {
343 : ACL_PROFILING_REG(acl::AclProfType::AclrtActiveStream);
344 : ACL_LOG_INFO("start to execute aclrtActiveStream");
345 : ACL_REQUIRES_NOT_NULL_WITH_INPUT_REPORT(activeStream);
346 : ACL_REQUIRES_NOT_NULL_WITH_INPUT_REPORT(stream);
347 :
348 : ACL_REQUIRES_RTS_OK(rtsActiveStream(static_cast<rtStream_t>(activeStream), static_cast<rtStream_t>(stream)));
349 :
350 : ACL_LOG_INFO("successfully execute aclrtActiveStream");
351 : return ACL_SUCCESS;
352 : }
353 :
354 : aclError aclrtSwitchStreamImpl(
355 : void* leftValue, aclrtCondition cond, void* rightValue, aclrtCompareDataType dataType, aclrtStream trueStream,
356 : aclrtStream falseStream, aclrtStream stream)
357 : {
358 : ACL_PROFILING_REG(acl::AclProfType::AclrtSwitchStream);
359 : ACL_LOG_INFO(
360 : "start to execute aclrtSwitchStream, cond is [%s], dataType is [%s]", acl::GetConditionDesc(cond),
361 : acl::GetCompareDataTypeDesc(dataType));
362 : ACL_REQUIRES_NOT_NULL_WITH_INPUT_REPORT(leftValue);
363 : ACL_REQUIRES_NOT_NULL_WITH_INPUT_REPORT(rightValue);
364 : ACL_REQUIRES_NOT_NULL_WITH_INPUT_REPORT(trueStream);
365 : ACL_REQUIRES_NOT_NULL_WITH_INPUT_REPORT(stream);
366 : ACL_CHECK_INVALID_PARAM_NO_VALUE(
367 : falseStream == nullptr, "falseStream", "falseStream is a reserved parameter and must be nullptr");
368 :
369 : ACL_REQUIRES_RTS_OK(rtsSwitchStream(
370 : leftValue, static_cast<rtCondition_t>(cond), rightValue, static_cast<rtSwitchDataType_t>(dataType),
371 : static_cast<rtStream_t>(trueStream), static_cast<rtStream_t>(falseStream), static_cast<rtStream_t>(stream)));
372 :
373 : ACL_LOG_INFO("successfully execute aclrtSwitchStream");
374 : return ACL_SUCCESS;
375 : }
376 :
377 : aclError aclrtStreamStopImpl(aclrtStream stream)
378 : {
379 : ACL_PROFILING_REG(acl::AclProfType::AclrtStreamStop);
380 : ACL_LOG_INFO("start to execute aclrtStreamStop");
381 :
382 : ACL_REQUIRES_RTS_OK(rtsStreamStop(static_cast<rtStream_t>(stream)));
383 : ACL_LOG_INFO("successfully execute aclrtStreamStop");
384 : return ACL_SUCCESS;
385 : }
386 :
387 : aclError aclrtPersistentTaskCleanImpl(aclrtStream stream)
388 : {
389 : ACL_PROFILING_REG(acl::AclProfType::AclrtPersistentTaskClean);
390 : ACL_LOG_INFO("start to execute aclrtPersistentTaskClean");
391 :
392 : ACL_REQUIRES_RTS_OK(rtsPersistentTaskClean(static_cast<rtStream_t>(stream)));
393 : ACL_LOG_INFO("successfully execute aclrtPersistentTaskClean");
394 : return ACL_SUCCESS;
395 : }
396 :
397 : aclError aclmdlRIGetTasksByStreamImpl(aclrtStream stream, aclmdlRITask* tasks, uint32_t* numTasks)
398 : {
399 : ACL_PROFILING_REG(acl::AclProfType::AclmdlRIGetTasksByStream);
400 :
401 : ACL_REQUIRES_RTS_OK(rtStreamGetTasks(static_cast<rtStream_t>(stream), static_cast<rtTask_t*>(tasks), numTasks));
402 :
403 : return ACL_SUCCESS;
404 : }
405 : #ifdef __cplusplus
406 : }
407 : #endif
|