1 /*
2  * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  */
23 
24 #include <assert.h>
25 #include <jni.h>
26 #include <jvmti.h>
27 #include <stdio.h>
28 #include "jni_tools.h"
29 
30 #ifdef __cplusplus
31 extern "C" {
32 #endif
33 
34 #define FIND_CLASS(_class, _className)\
35     if (!NSK_JNI_VERIFY(env, (_class = \
36             NSK_CPP_STUB2(FindClass, env, _className)) != NULL))\
37         return
38 
39 #define GET_OBJECT_CLASS(_class, _obj)\
40     if (!NSK_JNI_VERIFY(env, (_class = \
41             NSK_CPP_STUB2(GetObjectClass, env, _obj)) != NULL))\
42         return
43 
44 #define GET_STATIC_FIELD_ID(_fieldID, _class, _fieldName, _fieldSig)\
45     if (!NSK_JNI_VERIFY(env, (_fieldID = \
46             NSK_CPP_STUB4(GetStaticFieldID, env, _class,\
47                 _fieldName, _fieldSig)) != NULL))\
48         return
49 
50 #define GET_STATIC_OBJ_FIELD(_value, _class, _fieldName, _fieldSig)\
51     GET_STATIC_FIELD_ID(field, _class, _fieldName, _fieldSig);\
52     _value = NSK_CPP_STUB3(GetStaticObjectField, env, _class, \
53                                 field)
54 
55 #define GET_STATIC_BOOL_FIELD(_value, _class, _fieldName)\
56     GET_STATIC_FIELD_ID(field, _class, _fieldName, "Z");\
57     _value = NSK_CPP_STUB3(GetStaticBooleanField, env, _class, field)
58 
59 #define GET_FIELD_ID(_fieldID, _class, _fieldName, _fieldSig)\
60     if (!NSK_JNI_VERIFY(env, (_fieldID = \
61             NSK_CPP_STUB4(GetFieldID, env, _class,\
62                 _fieldName, _fieldSig)) != NULL))\
63         return
64 
65 #define GET_INT_FIELD(_value, _obj, _class, _fieldName)\
66     GET_FIELD_ID(field, _class, _fieldName, "I");\
67     _value = NSK_CPP_STUB3(GetIntField, env, _obj, field)
68 
69 #define GET_BOOL_FIELD(_value, _obj, _class, _fieldName)\
70     GET_FIELD_ID(field, _class, _fieldName, "Z");\
71     _value = NSK_CPP_STUB3(GetBooleanField, env, _obj, field)
72 
73 #define GET_LONG_FIELD(_value, _obj, _class, _fieldName)\
74     GET_FIELD_ID(field, _class, _fieldName, "J");\
75     _value = NSK_CPP_STUB3(GetLongField, env, _obj, field)
76 
77 #define GET_STATIC_INT_FIELD(_value, _class, _fieldName)\
78     GET_STATIC_FIELD_ID(field, _class, _fieldName, "I");\
79     _value = NSK_CPP_STUB3(GetStaticIntField, env, _class, field)
80 
81 #define SET_INT_FIELD(_obj, _class, _fieldName, _newValue)\
82     GET_FIELD_ID(field, _class, _fieldName, "I");\
83     NSK_CPP_STUB4(SetIntField, env, _obj, field, _newValue)
84 
85 #define GET_OBJ_FIELD(_value, _obj, _class, _fieldName, _fieldSig)\
86     GET_FIELD_ID(field, _class, _fieldName, _fieldSig);\
87     _value = NSK_CPP_STUB3(GetObjectField, env, _obj, field)
88 
89 
90 #define GET_ARR_ELEMENT(_arr, _index)\
91     NSK_CPP_STUB3(GetObjectArrayElement, env, _arr, _index)
92 
93 #define SET_ARR_ELEMENT(_arr, _index, _newValue)\
94     NSK_CPP_STUB4(SetObjectArrayElement, env, _arr, _index, _newValue)
95 
96 #define GET_STATIC_METHOD_ID(_methodID, _class, _methodName, _sig)\
97     if (!NSK_JNI_VERIFY(env, (_methodID = \
98             NSK_CPP_STUB4(GetStaticMethodID, env, _class,\
99                 _methodName, _sig)) != NULL))\
100         return
101 
102 #define GET_METHOD_ID(_methodID, _class, _methodName, _sig)\
103     if (!NSK_JNI_VERIFY(env, (_methodID = \
104             NSK_CPP_STUB4(GetMethodID, env, _class,\
105                 _methodName, _sig)) != NULL))\
106         return
107 
108 #define CALL_STATIC_VOID_NOPARAM(_class, _methodName)\
109     GET_STATIC_METHOD_ID(method, _class, _methodName, "()V");\
110     if (!NSK_JNI_VERIFY_VOID(env, NSK_CPP_STUB3(CallStaticVoidMethod, env,\
111                             _class, method)))\
112         return
113 
114 #define CALL_STATIC_VOID(_class, _methodName, _sig, _param)\
115     GET_STATIC_METHOD_ID(method, _class, _methodName, _sig);\
116     if (!NSK_JNI_VERIFY_VOID(env, NSK_CPP_STUB4(CallStaticVoidMethod, env,\
117                                                     _class, method, _param)))\
118         return
119 
120 #define CALL_STATIC_OBJ(_value, _class, _methodName, _sig, _param)\
121     GET_STATIC_METHOD_ID(method, _class, _methodName, _sig);\
122     _value = NSK_CPP_STUB4(CallStaticObjectMethod, env, _class, method, _param)
123 
124 #define CALL_VOID_NOPARAM(_obj, _class, _methodName)\
125     GET_METHOD_ID(method, _class, _methodName, "()V");\
126     if (!NSK_JNI_VERIFY_VOID(env, NSK_CPP_STUB3(CallVoidMethod, env, _obj,\
127                                                     method)))\
128         return
129 
130 #define CALL_VOID(_obj, _class, _methodName, _sig, _param)\
131     GET_METHOD_ID(method, _class, _methodName, "()V");\
132     if (!NSK_JNI_VERIFY_VOID(env, NSK_CPP_STUB4(CallVoidMethod, env, _obj,\
133                                                     method, _param)))\
134         return
135 
136 #define CALL_VOID2(_obj, _class, _methodName, _sig, _param1, _param2)\
137     GET_METHOD_ID(method, _class, _methodName, _sig);\
138     if (!NSK_JNI_VERIFY_VOID(env, NSK_CPP_STUB5(CallVoidMethod, env, _obj, \
139                                                     method, _param1, _param2)))\
140         return
141 
142 #define CALL_INT_NOPARAM(_value, _obj, _class, _methodName)\
143     GET_METHOD_ID(method, _class, _methodName, "()I");\
144     _value = NSK_CPP_STUB3(CallIntMethod, env, _obj, method)
145 
146 #define NEW_OBJ(_obj, _class, _constructorName, _sig, _params)\
147     GET_METHOD_ID(method, _class, _constructorName, _sig);\
148     if (!NSK_JNI_VERIFY(env, (_obj = \
149             NSK_CPP_STUB4(NewObject, env, _class, method, _params)) != NULL))\
150         return
151 
152 #define MONITOR_ENTER(x) \
153     NSK_JNI_VERIFY(env, NSK_CPP_STUB2(MonitorEnter, env, x) == 0)
154 
155 #define MONITOR_EXIT(x) \
156     NSK_JNI_VERIFY(env, NSK_CPP_STUB2(MonitorExit, env, x) == 0)
157 
158 #define TRACE(msg)\
159    GET_OBJ_FIELD(logger, obj, threadClass, "logger", "Lnsk/share/Log$Logger;");\
160    jmsg = NSK_CPP_STUB2(NewStringUTF, env, msg);\
161    CALL_VOID2(logger, loggerClass, "trace",\
162                            "(ILjava/lang/String;)V", 50, jmsg)
163 
164     static const char *SctrlClassName="nsk/monitoring/share/ThreadController";
165     static const char *SthreadControllerSig
166     = "Lnsk/monitoring/share/ThreadController;";
167 
168     static const char *SThreadsGroupLocksSig
169     ="Lnsk/monitoring/share/ThreadsGroupLocks;";
170     static const char *SThreadsGroupLocksClassName
171     ="nsk/monitoring/share/ThreadsGroupLocks";
172 
173 
174     static const char *SbringState_mn="bringState";
175     static const char *SnativeBringState_mn="nativeBringState";
176     static const char *SrecursiveMethod_mn="recursiveMethod";
177     static const char *SnativeRecursiveMethod_mn="nativeRecursiveMethod";
178     static const char *SloggerClassName = "nsk/share/Log$Logger";
179 
180     static const char *Snoparams="()V";
181     static const char *Slongparam="(J)V";
182 
183     /*
184      * Class:     nsk_monitoring_share_BaseThread
185      * Method:    nativeRecursiveMethod
186      * Signature: ()V
187      */
188     JNIEXPORT void JNICALL
Java_nsk_monitoring_share_BaseThread_nativeRecursiveMethod(JNIEnv * env,jobject obj)189     Java_nsk_monitoring_share_BaseThread_nativeRecursiveMethod(JNIEnv *env,
190             jobject obj) {
191         jint currDepth, maxDepth;
192         jobject logger;
193         jstring jmsg;
194         jfieldID field;
195         jmethodID method;
196 
197         jobject controller;
198         jclass threadClass, ctrlClass, loggerClass;
199 
200         int invocationType;
201 
202         GET_OBJECT_CLASS(threadClass, obj);
203         FIND_CLASS(ctrlClass, SctrlClassName);
204         FIND_CLASS(loggerClass, SloggerClassName);
205 
206 
207         /* currDepth++ */
208         GET_INT_FIELD(currDepth, obj, threadClass, "currentDepth");
209         currDepth++;
210         SET_INT_FIELD(obj, threadClass, "currentDepth", currDepth);
211 
212         GET_OBJ_FIELD(controller, obj, threadClass, "controller",
213                       SthreadControllerSig);
214         GET_INT_FIELD(maxDepth, controller, ctrlClass, "maxDepth");
215 
216         GET_STATIC_INT_FIELD(invocationType, ctrlClass, "invocationType");
217 
218         if (maxDepth - currDepth > 0)
219         {
220             CALL_STATIC_VOID_NOPARAM(threadClass, "yield");
221 
222             if (invocationType == 2/*MIXED_TYPE*/)
223             {
224                 CALL_VOID_NOPARAM(obj, threadClass, SrecursiveMethod_mn);
225             }
226             else
227             {
228                 CALL_VOID_NOPARAM(obj, threadClass, SnativeRecursiveMethod_mn);
229             }
230         }
231         else
232         {
233             TRACE("state has been reached");
234             if (invocationType == 2/*MIXED_TYPE*/)
235             {
236                 CALL_VOID_NOPARAM(obj, threadClass, SbringState_mn);
237             }
238             else
239             {
240                 CALL_VOID_NOPARAM(obj, threadClass, SnativeBringState_mn);
241             }
242         }
243 
244         currDepth--;
245         GET_OBJECT_CLASS(threadClass, obj);
246         SET_INT_FIELD(obj, threadClass, "currentDepth", currDepth);
247     }
248 
249     /*
250      * Class:     nsk_monitoring_share_BlockedThread
251      * Method:    nativeBringState
252      * Signature: ()V
253      */
254     JNIEXPORT void JNICALL
Java_nsk_monitoring_share_BlockedThread_nativeBringState(JNIEnv * env,jobject obj)255     Java_nsk_monitoring_share_BlockedThread_nativeBringState(JNIEnv *env,
256             jobject obj) {
257         jobject logger;
258         jstring jmsg;
259         jfieldID field;
260         jmethodID method;
261 
262         jclass threadClass, loggerClass;
263 
264         jobject STATE;
265 
266         //ThreadsGroupLocks:
267         jclass ThreadsGroupLocks;
268         jobject  threadsGroupLocks;
269         jmethodID getBarrier;
270 
271 
272         //CountDownLatch
273         jobject barrier;
274         jclass CountDownLatch;
275 
276         //Blocker
277         jobject blocker;
278         jclass Blocker;
279 
280         GET_OBJECT_CLASS(threadClass, obj);
281 
282         FIND_CLASS(loggerClass, SloggerClassName);
283         FIND_CLASS(ThreadsGroupLocks, SThreadsGroupLocksClassName);
284         FIND_CLASS(Blocker, "Lnsk/monitoring/share/ThreadsGroupLocks$Blocker;");
285         FIND_CLASS(CountDownLatch, "nsk/monitoring/share/ThreadsGroupLocks$PlainCountDownLatch");
286 
287         GET_OBJ_FIELD(threadsGroupLocks, obj, threadClass, "threadsGroupLocks", SThreadsGroupLocksSig);
288         GET_STATIC_OBJ_FIELD(STATE, threadClass, "STATE", "Ljava/lang/Thread$State;");
289         GET_OBJ_FIELD(blocker, threadsGroupLocks, ThreadsGroupLocks, "blocker", "Lnsk/monitoring/share/ThreadsGroupLocks$Blocker;");
290 
291         getBarrier = (*env)->GetMethodID(env, ThreadsGroupLocks, "getBarrier",
292             "(Ljava/lang/Thread$State;)Lnsk/monitoring/share/ThreadsGroupLocks$PlainCountDownLatch;");
293         barrier = (*env)->CallObjectMethod(env, threadsGroupLocks, getBarrier, STATE);
294 
295 
296         TRACE("entering to monitor");
297 
298         CALL_VOID_NOPARAM(barrier, CountDownLatch, "countDown");
299         CALL_VOID_NOPARAM(blocker, Blocker, "block");
300         TRACE("exiting from monitor");
301 
302     }
303 
304     /*
305      * Class:     nsk_monitoring_share_WaitingThread
306      * Method:    nativeBringState
307      * Signature: ()V
308      */
309     JNIEXPORT void JNICALL
Java_nsk_monitoring_share_WaitingThread_nativeBringState(JNIEnv * env,jobject obj)310     Java_nsk_monitoring_share_WaitingThread_nativeBringState(JNIEnv *env,
311             jobject obj) {
312         jobject logger;
313         jstring jmsg;
314         jfieldID field;
315         jmethodID method;
316 
317         jclass threadClass, loggerClass;
318 
319         //STATE
320         jobject STATE;
321 
322         //ThreadsGroupLocks:
323         jclass ThreadsGroupLocks;
324         jobject  threadsGroupLocks;
325         jmethodID getBarrier;
326 
327         //CountDownLatch
328         jobject barrier;
329         jclass CountDownLatch;
330 
331         GET_OBJECT_CLASS(threadClass, obj);
332 
333         FIND_CLASS(loggerClass, SloggerClassName);
334         FIND_CLASS(ThreadsGroupLocks, "nsk/monitoring/share/ThreadsGroupLocks");
335         FIND_CLASS(CountDownLatch, "nsk/monitoring/share/ThreadsGroupLocks$PlainCountDownLatch");
336 
337         GET_STATIC_OBJ_FIELD(STATE, threadClass, "STATE", "Ljava/lang/Thread$State;");
338         GET_OBJ_FIELD(threadsGroupLocks, obj, threadClass, "threadsGroupLocks", "Lnsk/monitoring/share/ThreadsGroupLocks;");
339 
340         getBarrier = (*env)->GetMethodID(env, ThreadsGroupLocks, "getBarrier",
341             "(Ljava/lang/Thread$State;)Lnsk/monitoring/share/ThreadsGroupLocks$PlainCountDownLatch;");
342         barrier = (*env)->CallObjectMethod(env, threadsGroupLocks, getBarrier, STATE);
343         CALL_VOID_NOPARAM(barrier, CountDownLatch, "countDown");
344 
345         TRACE("waiting on a monitor");
346         CALL_VOID_NOPARAM(barrier, CountDownLatch, "await");
347     }
348 
349     /*
350      * Class:     nsk_monitoring_share_SleepingThread
351      * Method:    nativeBringState
352      * Signature: ()V
353      */
354     JNIEXPORT void JNICALL
Java_nsk_monitoring_share_SleepingThread_nativeBringState(JNIEnv * env,jobject obj)355     Java_nsk_monitoring_share_SleepingThread_nativeBringState(JNIEnv *env,
356             jobject obj) {
357         jfieldID field;
358         jmethodID method;
359 
360         jclass threadClass, loggerClass;
361 
362         //STATE
363         jobject STATE;
364 
365         //ThreadsGroupLocks:
366         jclass ThreadsGroupLocks;
367         jobject  threadsGroupLocks;
368         jmethodID getBarrier;
369 
370         //CountDownLatch
371         jobject barrier;
372         jclass CountDownLatch;
373 
374         //Thread
375         jclass Thread;
376 
377         jlong sleepTime = 20 * 60 * 1000;
378 
379 
380         GET_OBJECT_CLASS(threadClass, obj);
381 
382         FIND_CLASS(loggerClass, SloggerClassName);
383         FIND_CLASS(ThreadsGroupLocks, "nsk/monitoring/share/ThreadsGroupLocks");
384         FIND_CLASS(CountDownLatch, "nsk/monitoring/share/ThreadsGroupLocks$PlainCountDownLatch");
385 
386         GET_STATIC_OBJ_FIELD(STATE, threadClass, "STATE", "Ljava/lang/Thread$State;");
387         GET_OBJ_FIELD(threadsGroupLocks, obj, threadClass, "threadsGroupLocks", "Lnsk/monitoring/share/ThreadsGroupLocks;");
388 
389         // Thread.sleep(3600 * 1000);
390         FIND_CLASS(Thread, "java/lang/Thread");
391 
392         getBarrier = (*env)->GetMethodID(env, ThreadsGroupLocks, "getBarrier",
393             "(Ljava/lang/Thread$State;)Lnsk/monitoring/share/ThreadsGroupLocks$PlainCountDownLatch;");
394         barrier = (*env)->CallObjectMethod(env, threadsGroupLocks, getBarrier, STATE);
395         CALL_VOID_NOPARAM(barrier, CountDownLatch, "countDown");
396 
397         CALL_STATIC_VOID(Thread, "sleep", "(J)V", sleepTime);
398     }
399 
400     /*
401      * Class:     nsk_monitoring_share_RunningThread
402      * Method:    nativeBringState
403      * Signature: ()V
404      */
405     JNIEXPORT void JNICALL
Java_nsk_monitoring_share_RunningThread_nativeBringState(JNIEnv * env,jobject obj)406     Java_nsk_monitoring_share_RunningThread_nativeBringState(JNIEnv *env,
407             jobject obj) {
408         jobject logger;
409         jstring jmsg;
410         jfieldID field;
411         jmethodID method;
412 
413         jclass threadClass, loggerClass;
414 
415         //STATE
416         jobject STATE;
417 
418         //ThreadsGroupLocks:
419         jclass ThreadsGroupLocks;
420         jobject  threadsGroupLocks;
421         jmethodID getBarrier;
422 
423         //CountDownLatch
424         jobject barrier;
425         jclass CountDownLatch;
426 
427         //Thread
428         jclass Thread;
429 
430         //runnableCanExit
431         jboolean flag = JNI_FALSE;
432 
433         GET_OBJECT_CLASS(threadClass, obj);
434 
435         FIND_CLASS(loggerClass, SloggerClassName);
436         FIND_CLASS(ThreadsGroupLocks, "nsk/monitoring/share/ThreadsGroupLocks");
437         FIND_CLASS(CountDownLatch, "nsk/monitoring/share/ThreadsGroupLocks$PlainCountDownLatch");
438 
439         GET_STATIC_OBJ_FIELD(STATE, threadClass, "STATE", "Ljava/lang/Thread$State;");
440         GET_OBJ_FIELD(threadsGroupLocks, obj, threadClass, "threadsGroupLocks", "Lnsk/monitoring/share/ThreadsGroupLocks;");
441 
442         // Thread.sleep(3600 * 1000);
443         FIND_CLASS(Thread, "java/lang/Thread");
444 
445         getBarrier = (*env)->GetMethodID(env, ThreadsGroupLocks, "getBarrier",
446             "(Ljava/lang/Thread$State;)Lnsk/monitoring/share/ThreadsGroupLocks$PlainCountDownLatch;");
447 
448         TRACE("running loop");
449 
450         barrier = (*env)->CallObjectMethod(env, threadsGroupLocks, getBarrier, STATE);
451         CALL_VOID_NOPARAM(barrier, CountDownLatch, "countDown");
452 
453         // while (!threadsGroupLocks.runnableCanExit.get()) {
454         //        Thread.yield();
455         //    }
456         while(flag==JNI_FALSE)
457         {
458             GET_BOOL_FIELD(flag, threadsGroupLocks, ThreadsGroupLocks, "runnableCanExit");
459             CALL_STATIC_VOID_NOPARAM(Thread, "yield");
460         }
461 
462     }
463 
getStateName(JNIEnv * env,jint state)464     jstring getStateName(JNIEnv *env, jint state) {
465         switch (state & JVMTI_JAVA_LANG_THREAD_STATE_MASK) {
466             case JVMTI_JAVA_LANG_THREAD_STATE_NEW:
467                 return (*env)->NewStringUTF(env,"NEW");
468             case JVMTI_JAVA_LANG_THREAD_STATE_TERMINATED:
469                 return (*env)->NewStringUTF(env,"TERMINATED");
470             case JVMTI_JAVA_LANG_THREAD_STATE_RUNNABLE:
471                 return (*env)->NewStringUTF(env,"RUNNABLE");
472             case JVMTI_JAVA_LANG_THREAD_STATE_BLOCKED:
473                 return (*env)->NewStringUTF(env,"BLOCKED");
474             case JVMTI_JAVA_LANG_THREAD_STATE_WAITING:
475                 return (*env)->NewStringUTF(env, "WAITING");
476             case JVMTI_JAVA_LANG_THREAD_STATE_TIMED_WAITING:
477                 return (*env)->NewStringUTF(env,"TIMED_WAITING");
478             }
479         // should never reach
480         assert(0);
481         return 0;
482     }
483 
484     /*
485      * Class:     nsk_monitoring_share_ThreadController
486      * Method:    getThreadState
487      * Signature: (Ljava/lang/Thread;)Ljava/lang/Thread/State;
488      */
489     JNIEXPORT jobject JNICALL
Java_nsk_monitoring_share_ThreadController_getThreadState(JNIEnv * env,jobject obj,jobject thread)490     Java_nsk_monitoring_share_ThreadController_getThreadState(JNIEnv *env,
491             jobject obj, jobject thread){
492 
493         JavaVM *vm;
494         jvmtiEnv *jvmti;
495         jclass ThreadState;
496         jmethodID method;
497         jobject threadState;
498         jstring stateName;
499         jint state;
500 
501         if(!NSK_VERIFY(
502              NSK_CPP_STUB2(GetJavaVM, env, &vm) == 0)) {
503             return NULL;
504         }
505 
506         if(!NSK_VERIFY(
507              NSK_CPP_STUB3(GetEnv, vm, (void **)&jvmti, JVMTI_VERSION_1)
508                     == JNI_OK)) {
509             return NULL;
510         }
511 
512         if(!NSK_VERIFY(
513              NSK_CPP_STUB3(GetThreadState, jvmti, (jthread)thread, &state)
514              == JVMTI_ERROR_NONE)) {
515             return NULL;
516         }
517 
518         stateName = getStateName(env, state);
519         if (!NSK_JNI_VERIFY(env, (ThreadState = NSK_CPP_STUB2(FindClass, env, "java/lang/Thread$State")) != NULL))
520             return NULL;
521 
522         if (!NSK_JNI_VERIFY(env, (method = NSK_CPP_STUB4(GetStaticMethodID, env, ThreadState, "valueOf", "(Ljava/lang/String;)Ljava/lang/Thread$State;")) != NULL))
523             return NULL;
524         threadState = NSK_CPP_STUB4(CallStaticObjectMethod, env, ThreadState, method, stateName);
525 
526         return threadState;
527     }
528 
529 #ifdef __cplusplus
530 }
531 #endif
532