1 /*
2  * Copyright (c) 2006-2019, RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author       Notes
8  * 2021-09.01     luckyzjq     the first version
9  * 2023-09-15     xqyjlj       change stack size in cpu64
10  */
11 #define __RT_IPC_SOURCE__
12 
13 #include <rtthread.h>
14 #include <stdlib.h>
15 #include "utest.h"
16 
17 #ifdef ARCH_CPU_64BIT
18 #define THREAD_STACKSIZE 8192
19 #else
20 #define THREAD_STACKSIZE 4096
21 #endif
22 
23 static struct rt_mutex static_mutex;
24 
25 #ifdef RT_USING_HEAP
26 static rt_mutex_t dynamic_mutex;
27 #endif /* RT_USING_HEAP */
28 
29 static volatile int _sync_flag;
30 
31 /* init test */
test_static_mutex_init(void)32 static void test_static_mutex_init(void)
33 {
34     rt_err_t result = -RT_ERROR;
35 
36     result = rt_mutex_init(&static_mutex, "static_mutex", RT_IPC_FLAG_PRIO);
37     if (RT_EOK != result)
38     {
39         uassert_true(RT_FALSE);
40     }
41 
42     result = rt_mutex_detach(&static_mutex);
43     if (RT_EOK != result)
44     {
45         uassert_true(RT_FALSE);
46     }
47 
48     result = rt_mutex_init(&static_mutex, "static_mutex", RT_IPC_FLAG_PRIO);
49     if (RT_EOK != result)
50     {
51         uassert_true(RT_FALSE);
52     }
53 
54     result = rt_mutex_detach(&static_mutex);
55     if (RT_EOK != result)
56     {
57         uassert_true(RT_FALSE);
58     }
59 
60     uassert_true(RT_TRUE);
61 }
62 
63 /* static take test */
static_mutex_take_entry(void * param)64 static void static_mutex_take_entry(void *param)
65 {
66     rt_err_t result;
67     rt_mutex_t mutex;
68 
69     int rand_num = rand() % 0x1000;
70     mutex = (rt_mutex_t)param;
71 
72     result = rt_mutex_take(mutex, rand_num);
73     if (RT_EOK == result)
74     {
75         uassert_true(RT_FALSE);
76     }
77     _sync_flag++;
78 }
79 
test_static_mutex_take(void)80 static void test_static_mutex_take(void)
81 {
82     rt_err_t result;
83 
84     _sync_flag = 0;
85 
86     result = rt_mutex_init(&static_mutex, "static_mutex", RT_IPC_FLAG_PRIO);
87     if (RT_EOK != result)
88     {
89         uassert_true(RT_FALSE);
90         return;
91     }
92 
93     /* take mutex and not release */
94     result = rt_mutex_take(&static_mutex, RT_WAITING_FOREVER);
95     if (RT_EOK != result)
96         uassert_true(RT_FALSE);
97 
98     rt_thread_t tid = rt_thread_create("mutex_th",
99                                        static_mutex_take_entry,
100                                        &static_mutex,
101                                        THREAD_STACKSIZE,
102                                        10,
103                                        10);
104     if (RT_NULL == tid)
105     {
106         uassert_true(RT_FALSE);
107         return;
108     }
109 
110     /* startup thread take second */
111     rt_thread_startup(tid);
112 
113     while (_sync_flag != 1)
114     {
115         rt_thread_mdelay(10);
116     }
117 
118     result = rt_mutex_detach(&static_mutex);
119     if (RT_EOK != result)
120         uassert_true(RT_FALSE);
121 
122     uassert_true(RT_TRUE);
123 }
124 
125 /* static release test */
static_mutex_release_entry(void * param)126 static void static_mutex_release_entry(void *param)
127 {
128     rt_err_t result;
129     rt_mutex_t mutex;
130 
131     int rand_num = rand() % 0x1000;
132     mutex = (rt_mutex_t)param;
133 
134     result = rt_mutex_take(mutex, rand_num);
135     if (RT_EOK != result)
136     {
137         uassert_true(RT_FALSE);
138     }
139     _sync_flag++;
140 }
test_static_mutex_release(void)141 static void test_static_mutex_release(void)
142 {
143     rt_err_t result;
144 
145     _sync_flag = 0;
146 
147     result = rt_mutex_init(&static_mutex, "static_mutex", RT_IPC_FLAG_PRIO);
148     if (RT_EOK != result)
149     {
150         uassert_true(RT_FALSE);
151         return;
152     }
153 
154     result = rt_mutex_release(&static_mutex);
155     uassert_true(result < 0);
156 
157     /* take mutex */
158     result = rt_mutex_take(&static_mutex, RT_WAITING_FOREVER);
159     if (RT_EOK != result)
160         uassert_true(RT_FALSE);
161 
162     /* release mutex */
163     result = rt_mutex_release(&static_mutex);
164     if (RT_EOK != result)
165         uassert_true(RT_FALSE);
166 
167     rt_thread_t tid = rt_thread_create("mutex_th",
168                                        static_mutex_release_entry,
169                                        &static_mutex,
170                                        THREAD_STACKSIZE,
171                                        10,
172                                        10);
173     if (RT_NULL == tid)
174     {
175         uassert_true(RT_FALSE);
176         return;
177     }
178 
179     /* startup thread and take mutex second */
180     rt_thread_startup(tid);
181 
182     while (_sync_flag != 1)
183     {
184         rt_thread_mdelay(10);
185     }
186 
187     result = rt_mutex_detach(&static_mutex);
188     if (RT_EOK != result)
189         uassert_true(RT_FALSE);
190 
191     uassert_true(RT_TRUE);
192 }
193 
194 /* static trytake test */
static_mutex_trytake_entry(void * param)195 static void static_mutex_trytake_entry(void *param)
196 {
197     rt_err_t result;
198     rt_mutex_t mutex;
199 
200     mutex = (rt_mutex_t)param;
201 
202     result = rt_mutex_trytake(mutex);
203     if (RT_EOK == result)
204     {
205         uassert_true(RT_FALSE);
206     }
207     _sync_flag++;
208 }
test_static_mutex_trytake(void)209 static void test_static_mutex_trytake(void)
210 {
211     rt_err_t result;
212 
213     _sync_flag = 0;
214 
215     result = rt_mutex_init(&static_mutex, "static_mutex", RT_IPC_FLAG_PRIO);
216     if (RT_EOK != result)
217     {
218         uassert_true(RT_FALSE);
219         return;
220     }
221 
222     /* take mutex and not release */
223     result = rt_mutex_take(&static_mutex, RT_WAITING_FOREVER);
224     if (RT_EOK != result)
225         uassert_true(RT_FALSE);
226 
227     rt_thread_t tid = rt_thread_create("mutex_th",
228                                        static_mutex_trytake_entry,
229                                        &static_mutex,
230                                        THREAD_STACKSIZE,
231                                        10,
232                                        10);
233     if (RT_NULL == tid)
234     {
235         uassert_true(RT_FALSE);
236         return;
237     }
238 
239     /* startup thread and trytake mutex second */
240     rt_thread_startup(tid);
241 
242     while (_sync_flag != 1)
243     {
244         rt_thread_mdelay(10);
245     }
246 
247     result = rt_mutex_detach(&static_mutex);
248     if (RT_EOK != result)
249         uassert_true(RT_FALSE);
250 
251     uassert_true(RT_TRUE);
252 }
253 
254 static rt_thread_t tid1 = RT_NULL;
255 static rt_thread_t tid2 = RT_NULL;
256 static rt_thread_t tid3 = RT_NULL;
257 
258 /* static mutex priority reverse test */
static_thread1_entry(void * param)259 static void static_thread1_entry(void *param)
260 {
261     /* let system schedule */
262     rt_thread_mdelay(100);
263 
264     /*  thread3 hode mutex  thread2 take mutex */
265     /* check thread2 and thread3 priority */
266     if (RT_SCHED_PRIV(tid2).current_priority != RT_SCHED_PRIV(tid3).current_priority)
267     {
268         uassert_true(RT_FALSE);
269     }
270     else
271     {
272         uassert_true(RT_TRUE);
273     }
274     _sync_flag++;
275 }
276 
static_thread2_entry(void * param)277 static void static_thread2_entry(void *param)
278 {
279     rt_err_t result;
280     rt_mutex_t mutex = (rt_mutex_t)param;
281 
282     /* let system schedule */
283     rt_thread_mdelay(50);
284 
285     result = rt_mutex_take(mutex, RT_WAITING_FOREVER);
286     if (result == RT_EOK)
287     {
288         rt_mutex_release(mutex);
289     }
290     _sync_flag++;
291 }
static_thread3_entry(void * param)292 static void static_thread3_entry(void *param)
293 {
294     rt_tick_t tick;
295     rt_err_t result;
296     rt_mutex_t mutex = (rt_mutex_t)param;
297 
298     result = rt_mutex_take(mutex, RT_WAITING_FOREVER);
299     if (result != RT_EOK)
300     {
301         uassert_true(RT_FALSE);
302     }
303 
304     tick = rt_tick_get();
305     while (rt_tick_get() - tick < (RT_TICK_PER_SECOND / 2));
306 
307     rt_mutex_release(mutex);
308     _sync_flag++;
309 }
310 
test_static_pri_reverse(void)311 static void test_static_pri_reverse(void)
312 {
313     rt_err_t result;
314     tid1 = RT_NULL;
315     tid2 = RT_NULL;
316     tid3 = RT_NULL;
317 
318     _sync_flag = 0;
319 
320     result = rt_mutex_init(&static_mutex, "static_mutex", RT_IPC_FLAG_PRIO);
321     if (RT_EOK != result)
322     {
323         uassert_true(RT_FALSE);
324         return;
325     }
326 
327     /* thread1 */
328     tid1 = rt_thread_create("thread1",
329                             static_thread1_entry,
330                             &static_mutex,
331                             UTEST_THR_STACK_SIZE,
332                             10 - 1,
333                             10);
334     if (tid1 != RT_NULL)
335         rt_thread_startup(tid1);
336 
337     /* thread2 */
338     tid2 = rt_thread_create("thread2",
339                             static_thread2_entry,
340                             &static_mutex,
341                             UTEST_THR_STACK_SIZE,
342                             10,
343                             10);
344     if (tid2 != RT_NULL)
345         rt_thread_startup(tid2);
346 
347     /* thread3 */
348     tid3 = rt_thread_create("thread3",
349                             static_thread3_entry,
350                             &static_mutex,
351                             UTEST_THR_STACK_SIZE,
352                             10 + 1,
353                             10);
354     if (tid3 != RT_NULL)
355         rt_thread_startup(tid3);
356 
357     while (_sync_flag != 3)
358     {
359         rt_thread_mdelay(10);
360     }
361 
362     result = rt_mutex_detach(&static_mutex);
363     if (RT_EOK != result)
364         uassert_true(RT_FALSE);
365 
366     uassert_true(RT_TRUE);
367 }
368 
369 /* create test */
test_dynamic_mutex_create(void)370 static void test_dynamic_mutex_create(void)
371 {
372     rt_err_t result = -RT_ERROR;
373 
374     /* PRIO mode */
375     dynamic_mutex = rt_mutex_create("dynamic_mutex", RT_IPC_FLAG_PRIO);
376     if (RT_NULL == dynamic_mutex)
377     {
378         uassert_true(RT_FALSE);
379     }
380 
381     result = rt_mutex_delete(dynamic_mutex);
382     if (RT_EOK != result)
383     {
384         uassert_true(RT_FALSE);
385     }
386 
387     /* FIFO mode */
388     dynamic_mutex = rt_mutex_create("dynamic_mutex", RT_IPC_FLAG_PRIO);
389     if (RT_NULL == dynamic_mutex)
390     {
391         uassert_true(RT_FALSE);
392     }
393 
394     result = rt_mutex_delete(dynamic_mutex);
395     if (RT_EOK != result)
396     {
397         uassert_true(RT_FALSE);
398     }
399 
400     uassert_true(RT_TRUE);
401 }
402 
403 /* dynamic take test */
dynamic_mutex_take_entry(void * param)404 static void dynamic_mutex_take_entry(void *param)
405 {
406     rt_err_t result;
407     rt_mutex_t mutex;
408 
409     int rand_num = rand() % 0x1000;
410     mutex = (rt_mutex_t)param;
411 
412     result = rt_mutex_take(mutex, rand_num);
413     if (RT_EOK == result)
414     {
415         uassert_true(RT_FALSE);
416     }
417     _sync_flag++;
418 }
419 
test_dynamic_mutex_take(void)420 static void test_dynamic_mutex_take(void)
421 {
422     rt_err_t result;
423 
424     _sync_flag = 0;
425 
426     dynamic_mutex = rt_mutex_create("dynamic_mutex", RT_IPC_FLAG_PRIO);
427     if (RT_NULL == dynamic_mutex)
428     {
429         uassert_true(RT_FALSE);
430         return;
431     }
432 
433     /* take mutex and not release */
434     result = rt_mutex_take(dynamic_mutex, RT_WAITING_FOREVER);
435     if (RT_EOK != result)
436         uassert_true(RT_FALSE);
437 
438     rt_thread_t tid = rt_thread_create("mutex_th",
439                                        dynamic_mutex_take_entry,
440                                        dynamic_mutex,
441                                        THREAD_STACKSIZE,
442                                        10,
443                                        10);
444     if (RT_NULL == tid)
445     {
446         uassert_true(RT_FALSE);
447         return;
448     }
449 
450     /* startup thread take second */
451     rt_thread_startup(tid);
452 
453     while (_sync_flag != 1)
454     {
455         rt_thread_mdelay(10);
456     }
457 
458     result = rt_mutex_delete(dynamic_mutex);
459     if (RT_EOK != result)
460         uassert_true(RT_FALSE);
461 
462     uassert_true(RT_TRUE);
463 }
464 
465 /* dynamic release test */
dynamic_mutex_release_entry(void * param)466 static void dynamic_mutex_release_entry(void *param)
467 {
468     rt_err_t result;
469     rt_mutex_t mutex;
470 
471     int rand_num = rand() % 0x1000;
472     mutex = (rt_mutex_t)param;
473 
474     result = rt_mutex_take(mutex, rand_num);
475     if (RT_EOK != result)
476     {
477         uassert_true(RT_FALSE);
478     }
479     _sync_flag++;
480 }
test_dynamic_mutex_release(void)481 static void test_dynamic_mutex_release(void)
482 {
483     rt_err_t result;
484 
485     _sync_flag = 0;
486     dynamic_mutex = rt_mutex_create("dynamic_mutex", RT_IPC_FLAG_PRIO);
487     if (RT_NULL == dynamic_mutex)
488     {
489         uassert_true(RT_FALSE);
490         return;
491     }
492 
493     result = rt_mutex_release(dynamic_mutex);
494     uassert_true(result < 0);
495 
496     /* take mutex */
497     result = rt_mutex_take(dynamic_mutex, RT_WAITING_FOREVER);
498     if (RT_EOK != result)
499         uassert_true(RT_FALSE);
500 
501     /* release mutex */
502     result = rt_mutex_release(dynamic_mutex);
503     if (RT_EOK != result)
504         uassert_true(RT_FALSE);
505 
506     rt_thread_t tid = rt_thread_create("mutex_th",
507                                        dynamic_mutex_release_entry,
508                                        dynamic_mutex,
509                                        THREAD_STACKSIZE,
510                                        10,
511                                        10);
512     if (RT_NULL == tid)
513     {
514         uassert_true(RT_FALSE);
515         return;
516     }
517 
518     /* startup thread and take mutex second */
519     rt_thread_startup(tid);
520 
521     while (_sync_flag != 1)
522     {
523         rt_thread_mdelay(10);
524     }
525 
526     result = rt_mutex_delete(dynamic_mutex);
527     if (RT_EOK != result)
528         uassert_true(RT_FALSE);
529 
530     uassert_true(RT_TRUE);
531 }
532 
533 /* dynamic trytake test */
dynamic_mutex_trytake_entry(void * param)534 static void dynamic_mutex_trytake_entry(void *param)
535 {
536     rt_err_t result;
537     rt_mutex_t mutex;
538 
539     mutex = (rt_mutex_t)param;
540 
541     result = rt_mutex_trytake(mutex);
542     if (RT_EOK == result)
543     {
544         uassert_true(RT_FALSE);
545     }
546     _sync_flag++;
547 }
test_dynamic_mutex_trytake(void)548 static void test_dynamic_mutex_trytake(void)
549 {
550     rt_err_t result;
551 
552     _sync_flag = 0;
553     dynamic_mutex = rt_mutex_create("dynamic_mutex", RT_IPC_FLAG_PRIO);
554     if (RT_NULL == dynamic_mutex)
555     {
556         uassert_true(RT_FALSE);
557         return;
558     }
559 
560     /* take mutex and not release */
561     result = rt_mutex_take(dynamic_mutex, RT_WAITING_FOREVER);
562     if (RT_EOK != result)
563         uassert_true(RT_FALSE);
564 
565     rt_thread_t tid = rt_thread_create("mutex_th",
566                                        dynamic_mutex_trytake_entry,
567                                        dynamic_mutex,
568                                        THREAD_STACKSIZE,
569                                        10,
570                                        10);
571     if (RT_NULL == tid)
572     {
573         uassert_true(RT_FALSE);
574         return;
575     }
576 
577     /* startup thread and trytake mutex second */
578     rt_thread_startup(tid);
579 
580     while (_sync_flag != 1)
581     {
582         rt_thread_mdelay(10);
583     }
584 
585     result = rt_mutex_delete(dynamic_mutex);
586     if (RT_EOK != result)
587         uassert_true(RT_FALSE);
588 
589     uassert_true(RT_TRUE);
590 }
591 
592 /* dynamic mutex priority reverse test */
dynamic_thread1_entry(void * param)593 static void dynamic_thread1_entry(void *param)
594 {
595     /* let system schedule */
596     rt_thread_mdelay(100);
597 
598     /*  thread3 hode mutex  thread2 take mutex */
599     /* check thread2 and thread3 priority */
600     if (RT_SCHED_PRIV(tid2).current_priority != RT_SCHED_PRIV(tid3).current_priority)
601     {
602         uassert_true(RT_FALSE);
603     }
604     else
605     {
606         uassert_true(RT_TRUE);
607     }
608     _sync_flag++;
609 }
610 
dynamic_thread2_entry(void * param)611 static void dynamic_thread2_entry(void *param)
612 {
613     rt_err_t result;
614     rt_mutex_t mutex = (rt_mutex_t)param;
615 
616     /* let system schedule */
617     rt_thread_mdelay(50);
618 
619     result = rt_mutex_take(mutex, RT_WAITING_FOREVER);
620     if (result == RT_EOK)
621     {
622         rt_mutex_release(mutex);
623     }
624     _sync_flag++;
625 }
dynamic_thread3_entry(void * param)626 static void dynamic_thread3_entry(void *param)
627 {
628     rt_tick_t tick;
629     rt_err_t result;
630     rt_mutex_t mutex = (rt_mutex_t)param;
631 
632     result = rt_mutex_take(mutex, RT_WAITING_FOREVER);
633     if (result != RT_EOK)
634     {
635         uassert_true(RT_FALSE);
636     }
637 
638     tick = rt_tick_get();
639     while (rt_tick_get() - tick < (RT_TICK_PER_SECOND / 2));
640 
641     rt_mutex_release(mutex);
642     _sync_flag++;
643 }
644 
test_dynamic_pri_reverse(void)645 static void test_dynamic_pri_reverse(void)
646 {
647     rt_err_t result;
648     tid1 = RT_NULL;
649     tid2 = RT_NULL;
650     tid3 = RT_NULL;
651 
652     _sync_flag = 0;
653     dynamic_mutex = rt_mutex_create("dynamic_mutex", RT_IPC_FLAG_PRIO);
654     if (RT_NULL == dynamic_mutex)
655     {
656         uassert_true(RT_FALSE);
657         return;
658     }
659 
660     /* thread1 */
661     tid1 = rt_thread_create("thread1",
662                             dynamic_thread1_entry,
663                             dynamic_mutex,
664                             UTEST_THR_STACK_SIZE,
665                             10 - 1,
666                             10);
667     if (tid1 != RT_NULL)
668         rt_thread_startup(tid1);
669 
670     /* thread2 */
671     tid2 = rt_thread_create("thread2",
672                             dynamic_thread2_entry,
673                             dynamic_mutex,
674                             UTEST_THR_STACK_SIZE,
675                             10,
676                             10);
677     if (tid2 != RT_NULL)
678         rt_thread_startup(tid2);
679 
680     /* thread3 */
681     tid3 = rt_thread_create("thread3",
682                             dynamic_thread3_entry,
683                             dynamic_mutex,
684                             UTEST_THR_STACK_SIZE,
685                             10 + 1,
686                             10);
687     if (tid3 != RT_NULL)
688         rt_thread_startup(tid3);
689 
690     while (_sync_flag != 3)
691     {
692         rt_thread_mdelay(10);
693     }
694 
695     result = rt_mutex_delete(dynamic_mutex);
696     if (RT_EOK != result)
697         uassert_true(RT_FALSE);
698 
699     uassert_true(RT_TRUE);
700 }
701 
recursive_lock_test_entry(void * param)702 static void recursive_lock_test_entry(void *param)
703 {
704     rt_err_t result;
705     rt_mutex_t mutex = (rt_mutex_t)param;
706 
707     result = rt_mutex_take(mutex, RT_WAITING_FOREVER);
708     uassert_true(result == RT_EOK);
709     uassert_true(_sync_flag == 0);
710     result = rt_mutex_take(mutex, RT_WAITING_FOREVER);
711     uassert_true(result == RT_EOK);
712     _sync_flag++;
713 }
714 
test_recurse_lock(void)715 static void test_recurse_lock(void)
716 {
717     rt_err_t result;
718 
719     _sync_flag = 0;
720     result = rt_mutex_init(&static_mutex, "static_mutex", RT_IPC_FLAG_PRIO);
721     uassert_true(result == RT_EOK);
722 
723     /* take mutex and not release */
724     result = rt_mutex_take(&static_mutex, RT_WAITING_FOREVER);
725     uassert_true(result == RT_EOK);
726 
727     /* take mutex twice */
728     result = rt_mutex_take(&static_mutex, RT_WAITING_FOREVER);
729     uassert_true(result == RT_EOK);
730 
731     rt_thread_t tid = rt_thread_create("mutex_th",
732                                        recursive_lock_test_entry,
733                                        &static_mutex,
734                                        THREAD_STACKSIZE,
735                                        10,
736                                        10);
737     _sync_flag = -1;
738 
739     if (tid != RT_NULL)
740         rt_thread_startup(tid);
741 
742     result = rt_mutex_release(&static_mutex);
743     uassert_true(result == RT_EOK);
744 
745     _sync_flag = 0;
746 
747     result = rt_mutex_release(&static_mutex);
748     uassert_true(result == RT_EOK);
749 
750     while (_sync_flag != 1)
751     {
752         rt_thread_mdelay(10);
753     }
754 
755     result = rt_mutex_take(&static_mutex, RT_WAITING_FOREVER);
756     uassert_true(result == RT_EOK);
757 
758     result = rt_mutex_detach(&static_mutex);
759     uassert_true(result == RT_EOK);
760 }
761 
utest_tc_init(void)762 static rt_err_t utest_tc_init(void)
763 {
764 #ifdef RT_USING_HEAP
765     dynamic_mutex = RT_NULL;
766 #endif /* RT_USING_HEAP */
767 
768     return RT_EOK;
769 }
770 
utest_tc_cleanup(void)771 static rt_err_t utest_tc_cleanup(void)
772 {
773 #ifdef RT_USING_HEAP
774     dynamic_mutex = RT_NULL;
775 #endif /* RT_USING_HEAP */
776 
777     return RT_EOK;
778 }
779 
testcase(void)780 static void testcase(void)
781 {
782     UTEST_UNIT_RUN(test_static_mutex_init);
783     UTEST_UNIT_RUN(test_static_mutex_take);
784     UTEST_UNIT_RUN(test_static_mutex_release);
785     UTEST_UNIT_RUN(test_static_mutex_trytake);
786     UTEST_UNIT_RUN(test_static_pri_reverse);
787 #ifdef RT_USING_HEAP
788     UTEST_UNIT_RUN(test_dynamic_mutex_create);
789     UTEST_UNIT_RUN(test_dynamic_mutex_take);
790     UTEST_UNIT_RUN(test_dynamic_mutex_release);
791     UTEST_UNIT_RUN(test_dynamic_mutex_trytake);
792     UTEST_UNIT_RUN(test_dynamic_pri_reverse);
793 #endif
794     UTEST_UNIT_RUN(test_recurse_lock);
795 }
796 UTEST_TC_EXPORT(testcase, "testcases.kernel.mutex_tc", utest_tc_init, utest_tc_cleanup, 1000);
797 
798 /********************* end of file ************************/
799