3fd3ab1c2fb2d774833f3b5cd870586f18e51625
[cacao.git] / threads / thread.c
1 /* -*- mode: c; tab-width: 4; c-basic-offset: 4 -*- */
2 /*
3  * thread.c
4  * Thread support.
5  *
6  * Copyright (c) 1996 T. J. Wilkinson & Associates, London, UK.
7  *
8  * See the file "license.terms" for information on usage and redistribution
9  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
10  *
11  * Written by Tim Wilkinson <tim@tjwassoc.demon.co.uk>, 1996.
12  */
13
14 #include "global.h"
15
16 #if !defined(NATIVE_THREADS)
17
18 #include <stdlib.h>
19 #include <string.h>
20 #include <assert.h>
21 #include <sys/types.h>
22 #include <sys/mman.h>                   /* for mprotect */
23 #include <unistd.h>
24 #include <signal.h>
25 #include <sys/time.h>
26 #include <time.h>
27 #include <errno.h>
28
29 #include "config.h"
30 #include "thread.h"
31 #include "locks.h"
32 #include "tables.h"
33 #include "native.h"
34 #include "loader.h"
35 #include "builtin.h"
36 #include "asmpart.h"
37 #include "toolbox/logging.h"
38 #include "toolbox/memory.h"
39 #include "toolbox/avl.h"
40
41 static classinfo *class_java_lang_ThreadDeath;
42
43 thread* currentThread = NULL;
44 thread* mainThread;
45 thread* threadQhead[MAX_THREAD_PRIO + 1];
46 thread* threadQtail[MAX_THREAD_PRIO + 1];
47
48 thread* liveThreads = NULL;
49 thread* sleepThreads = NULL;
50
51 int blockInts;
52 bool needReschedule;
53
54 ctx contexts[MAXTHREADS];
55
56 /* Number of threads alive, also counting daemons */
57 static int talive;
58
59 /* Number of daemon threads alive */
60 static int tdaemon;
61
62 static void firstStartThread(void);
63
64 void reschedule(void);
65
66 /* Setup default thread stack size - this can be overwritten if required */
67 int threadStackSize = THREADSTACKSIZE;
68
69 /* Pointer to the stack of the last killed thread. The free is delayed. */
70 void *stack_to_be_freed = 0;
71
72 static thread* startDaemon(void* func, char* nm, int stackSize);
73
74 /*
75  * Allocate the stack for a thread
76  */
77 void
78 allocThreadStack (thread *tid, int size)
79 {
80     int pageSize = getpagesize();
81     unsigned long pageBegin;
82
83         assert(stack_to_be_freed == 0);
84
85     CONTEXT(tid).stackMem = GCNEW(u1, size + 4 * pageSize);
86     assert(CONTEXT(tid).stackMem != 0);
87     CONTEXT(tid).stackEnd = CONTEXT(tid).stackMem + size + 2 * pageSize;
88     
89     pageBegin = (unsigned long)(CONTEXT(tid).stackMem) + pageSize - 1;
90     pageBegin = pageBegin - pageBegin % pageSize;
91
92     CONTEXT(tid).stackBase = (u1*)pageBegin + pageSize;
93 }
94
95 /*
96  * Mark the stack for a thread to be freed. We cannot free the stack
97  * immediately because it is still in use!
98  */
99 void
100 freeThreadStack (thread *tid)
101 {
102     if (!(CONTEXT(tid).flags & THREAD_FLAGS_NOSTACKALLOC))
103     {
104                 int pageSize = getpagesize();
105                 unsigned long pageBegin;
106
107                 pageBegin = (unsigned long)(CONTEXT(tid).stackMem) + pageSize - 1;
108                 pageBegin = pageBegin - pageBegin % pageSize;
109
110                 assert(stack_to_be_freed == 0);
111
112                 stack_to_be_freed = CONTEXT(tid).stackMem;
113     }
114     CONTEXT(tid).stackMem = 0;
115     CONTEXT(tid).stackBase = 0;
116     CONTEXT(tid).stackEnd = 0;
117 }
118
119 /*
120  * Initialize threads.
121  */
122 void
123 initThreads(u1 *stackbottom)
124 {
125         thread *the_main_thread;
126     int i;
127         char mainname[] = "main";
128         /*int len = strlen(mainname);*/
129
130         signal(SIGPIPE, SIG_IGN);
131
132     initLocks();
133
134     for (i = 0; i < MAXTHREADS; ++i) {
135                 contexts[i].free = true;
136                 contexts[i].thread = NULL;
137         }
138
139     /* Allocate a thread to be the main thread */
140     liveThreads = the_main_thread = 
141         (thread *) builtin_new(class_new(utf_new_char("java/lang/Thread")));
142     assert(the_main_thread != 0);
143     
144     the_main_thread->PrivateInfo = 1;
145     CONTEXT(the_main_thread).free = false;
146
147 #if 0
148     {
149         /* stefan */
150         methodinfo *m;
151         m = class_fetchmethod(
152                         class_java_lang_String,
153                         utf_new_char ("toCharArray"),
154                         utf_new_char ("()[C")
155                         );
156 printf("DEADCODE LIVES ?????????\n");fflush(stdout);
157         the_main_thread->name = asm_calljavafunction (m, javastring_new(utf_new_char("main")), 0, 0, 0);
158     }
159 #endif
160         the_main_thread->name=javastring_new(utf_new_char(mainname));
161 /*      the_main_thread->name = builtin_newarray_char(len);
162         {   u2 *d = the_main_thread->name->data;
163                 for (i=0; i<len; i++)
164                         d[i] = mainname[i];
165         }*/
166     the_main_thread->priority = NORM_THREAD_PRIO;
167     CONTEXT(the_main_thread).priority = (u1)the_main_thread->priority;
168     CONTEXT(the_main_thread).texceptionptr = 0;
169     the_main_thread->next = 0;
170     CONTEXT(the_main_thread).status = THREAD_SUSPENDED;
171     CONTEXT(the_main_thread).stackBase = CONTEXT(the_main_thread).stackEnd = stackbottom;
172     THREADINFO(&CONTEXT(the_main_thread));
173
174     DBG( printf("main thread %p base %p end %p\n", 
175                                 the_main_thread,
176                                 CONTEXT(the_main_thread).stackBase,
177                                 CONTEXT(the_main_thread).stackEnd); );
178
179         CONTEXT(the_main_thread).flags = THREAD_FLAGS_NOSTACKALLOC;
180         CONTEXT(the_main_thread).nextlive = 0;
181         CONTEXT(the_main_thread).thread = the_main_thread;
182         /*the_main_thread->single_step = 0;*/
183         the_main_thread->daemon = 0;
184         /*the_main_thread->stillborn = 0;*/
185         /*the_main_thread->target = 0;*/
186
187         the_main_thread->contextClassLoader = 0;
188         /*the_main_thread->inheritedAccessControlContext = 0;*/
189         /*the_main_thread->values = 0;*/
190
191         /* Allocate and init ThreadGroup */
192         the_main_thread->group =
193         (threadGroup *) native_new_and_init(class_load(class_new(utf_new_char("java/lang/ThreadGroup"))));
194         assert(the_main_thread->group != 0);
195
196         talive++;
197
198         /* Load exception classes */
199 /*      loader_load_sysclass(&class_java_lang_ThreadDeath, */
200 /*                           utf_new_char("java/lang/ThreadDeath")); */
201     class_java_lang_ThreadDeath =
202         class_load(class_new(utf_new_char("java/lang/ThreadDeath")));
203
204         DBG( fprintf(stderr, "finishing initThreads\n"); );
205
206     mainThread = currentThread = the_main_thread;
207
208         /* Add thread into runQ */
209         iresumeThread(mainThread);
210
211         assert(blockInts == 0);
212 }
213
214 /*
215  * Start a new thread running.
216  */
217 void
218 startThread (thread* tid)
219 {
220     int i;
221
222     /* Allocate a stack context */
223     for (i = 0; i < MAXTHREADS; ++i)
224                 if (contexts[i].free)
225                         break;
226
227     if (i == MAXTHREADS)
228                 panic("Too many threads");
229
230         assert(tid->priority >= MIN_THREAD_PRIO && tid->priority <= MAX_THREAD_PRIO);
231
232     tid->PrivateInfo = i + 1;
233     CONTEXT(tid).free = false;
234         CONTEXT(tid).thread = tid;
235     CONTEXT(tid).nextlive = liveThreads;
236     liveThreads = tid;
237     allocThreadStack(tid, threadStackSize);
238     CONTEXT(tid).usedStackTop = CONTEXT(tid).stackBase;
239     CONTEXT(tid).flags = THREAD_FLAGS_GENERAL;
240     CONTEXT(tid).status = THREAD_SUSPENDED;
241     CONTEXT(tid).priority = (u1)tid->priority;
242     CONTEXT(tid).texceptionptr = 0;
243
244     /* Construct the initial restore point. */
245     THREADINIT((&CONTEXT(tid)), firstStartThread);
246
247     DBG( printf("new thread %p base %p end %p\n",
248                                 tid, CONTEXT(tid).stackBase,
249                                 CONTEXT(tid).stackEnd); );
250
251         talive++;
252         if (tid->daemon)
253                 tdaemon++;
254
255         /* Add thread into runQ */
256         iresumeThread(tid);
257 }
258
259
260 /*
261  * Start a daemon thread.
262  */
263 static thread *startDaemon(void* func, char* nm, int stackSize)
264 {
265     thread* tid;
266     int i;
267
268     DBG( printf("startDaemon %s\n", nm); );
269
270         tid = (thread *) builtin_new(class_new(utf_new_char("java/lang/Thread")));
271         assert(tid != 0);
272
273         for (i = 0; i < MAXTHREADS; ++i)
274                 if (contexts[i].free)
275                         break;
276         if (i == MAXTHREADS)
277                 panic("Too many threads");
278
279         tid->PrivateInfo = i + 1;
280         CONTEXT(tid).free = false;
281         tid->name = 0;          /* for the moment */
282         tid->priority = MAX_THREAD_PRIO;
283         CONTEXT(tid).priority = (u1)tid->priority;
284         tid->next = 0;
285         CONTEXT(tid).status = THREAD_SUSPENDED;
286
287         allocThreadStack(tid, stackSize);
288         /*tid->single_step = 0;*/
289         tid->daemon = 1;
290         /*tid->stillborn = 0;*/
291         /*tid->target = 0;*/
292         tid->group = 0;
293
294         /* Construct the initial restore point. */
295         THREADINIT((&CONTEXT(tid)), func);
296
297         talive++;
298         tdaemon++;
299
300         return tid;
301 }
302
303 /*
304  * All threads start here.
305  */
306 static void
307 firstStartThread(void)
308 {
309     methodinfo *method;
310
311     DBG( printf("firstStartThread %p\n", currentThread); );
312
313         if (stack_to_be_freed != 0)     {
314                 stack_to_be_freed = 0;
315         }
316
317         /* Every thread starts with the interrupts off */
318         intsRestore();
319         assert(blockInts == 0);
320
321         /* Find the run()V method and call it */
322         method = class_findmethod(currentThread->header.vftbl->class,
323                                                           utf_new_char("run"), utf_new_char("()V"));
324         if (method == 0)
325                 panic("Cannot find method \'void run ()\'");
326
327         asm_calljavafunction(method, currentThread, NULL, NULL, NULL);
328
329     if (*exceptionptr) {
330         utf_display((*exceptionptr)->vftbl->class->name);
331         printf("\n");
332     }
333
334         killThread(0);
335         assert("Thread returned from killThread" == 0);
336 }
337
338 /*
339  * Resume a thread running.
340  * This routine has to be called only from locations which ensure
341  * run / block queue consistency. There is no check for illegal resume
342  * conditions (like explicitly resuming an IO blocked thread). There also
343  * is no update of any blocking queue. Both has to be done by the caller
344  */
345 void
346 iresumeThread(thread* tid)
347 {
348     DBG( printf("resumeThread %p\n", tid); );
349
350         intsDisable();
351
352         if (CONTEXT(tid).status != THREAD_RUNNING)
353         {
354                 CONTEXT(tid).status = THREAD_RUNNING;
355
356                 DBG( fprintf(stderr, "prio is %d\n", CONTEXT(tid).priority); );
357
358                 /* Place thread on the end of its queue */
359                 if (threadQhead[CONTEXT(tid).priority] == 0) {
360                         threadQhead[CONTEXT(tid).priority] = tid;
361                         threadQtail[CONTEXT(tid).priority] = tid;
362                         if (CONTEXT(tid).priority
363                                 > CONTEXT(currentThread).priority)
364                                 needReschedule = true;
365                 }
366                 else
367                 {
368                         threadQtail[CONTEXT(tid).priority]->next = tid;
369                         threadQtail[CONTEXT(tid).priority] = tid;
370                 }
371                 tid->next = 0;
372         }
373         SDBG( else { printf("Re-resuming %p\n", tid); } );
374
375         intsRestore();
376 }
377
378 /*
379  * Yield process to another thread of equal priority.
380  */
381 void
382 yieldThread()
383 {
384     intsDisable();
385
386     if (threadQhead[CONTEXT(currentThread).priority]
387                 != threadQtail[CONTEXT(currentThread).priority])
388     {
389                 /* Get the next thread and move me to the end */
390                 threadQhead[CONTEXT(currentThread).priority] = currentThread->next;
391                 threadQtail[CONTEXT(currentThread).priority]->next = currentThread;
392                 threadQtail[CONTEXT(currentThread).priority] = currentThread;
393                 currentThread->next = 0;
394                 needReschedule = true;
395     }
396
397     intsRestore();
398 }
399
400 /*
401  * Explicit request by user to resume a thread
402  * The definition says that it is just legal to call this after a preceeding
403  * suspend (which got through). If the thread was blocked for some other
404  * reason (either sleep or IO or a muxSem), we simply can't do it
405  * We use a new thread flag THREAD_FLAGS_USER_SUSPEND for this purpose
406  * (which is set by suspendThread(.))
407  */
408 void
409 resumeThread (thread* tid)
410 {
411     if ((CONTEXT(tid).flags & THREAD_FLAGS_USER_SUSPEND) != 0)
412     {
413                 intsDisable();
414                 CONTEXT(tid).flags &= ~THREAD_FLAGS_USER_SUSPEND;
415                 iresumeThread(tid);
416                 intsRestore();
417     }
418 }
419
420 /*
421  * Suspend a thread.
422  * This is an explicit user request to suspend the thread - the counterpart
423  * for resumeThreadRequest(.). It is JUST called by the java method
424  * Thread.suspend()
425  * What makes it distinct is the fact that the suspended thread is not contained
426  * in any block queue. Without a special flag (indicating the user suspend), we
427  * can't check s suspended thread for this condition afterwards (which is
428  * required by resumeThreadRequest()). The new thread flag
429  * THREAD_FLAGS_USER_SUSPEND is used for this purpose.
430  */
431 void
432 suspendThread(thread* tid)
433 {
434     thread** ntid;
435
436     intsDisable();
437
438     if (CONTEXT(tid).status != THREAD_SUSPENDED)
439     {
440                 CONTEXT(tid).status = THREAD_SUSPENDED;
441                 
442                 /*
443                  * This is used to indicate the explicit suspend condition
444                  * required by resumeThreadRequest()
445                  */
446                 CONTEXT(tid).flags |= THREAD_FLAGS_USER_SUSPEND;
447
448                 for (ntid = &threadQhead[CONTEXT(tid).priority];
449                          *ntid != 0;
450                          ntid = &(*ntid)->next)
451                 {
452                         if (*ntid == tid)
453                         {
454                                 *ntid = tid->next;
455                                 tid->next = 0;
456                                 if (tid == currentThread)
457                                 {
458                                         reschedule();
459                                 }
460                                 break;
461                         }
462                 }
463     }
464         SDBG( else { printf("Re-suspending %p\n", tid); } );
465
466         intsRestore();
467 }
468
469 /*
470  * Suspend a thread on a queue.
471  */
472 void
473 suspendOnQThread(thread* tid, thread** queue)
474 {
475     thread** ntid;
476
477         DBG( printf("suspendOnQThread %p %p\n", tid, queue); );
478
479         assert(blockInts > 0);
480
481         if (CONTEXT(tid).status != THREAD_SUSPENDED)
482         {
483                 CONTEXT(tid).status = THREAD_SUSPENDED;
484
485                 for (ntid = &threadQhead[CONTEXT(tid).priority];
486                          *ntid != 0;
487                          ntid = &(*ntid)->next)
488                 {
489                         if (*ntid == tid)
490                         {
491                                 *ntid = tid->next;
492                                 /* Insert onto head of lock wait Q */
493                                 tid->next = *queue;
494                                 *queue = tid;
495                                 if (tid == currentThread)
496                                 {
497                                         DBG( fprintf(stderr, "suspending %p (cur=%p) with prio %d\n",
498                                                                  tid, currentThread, CONTEXT(tid).priority); );
499                                         reschedule();
500                                 }
501                                 break;
502                         }
503                 }
504         }
505         SDBG( else { printf("Re-suspending %p on %p\n", tid, *queue); } );
506 }
507
508 /*
509  * Kill thread.
510  */
511 void
512 killThread(thread* tid)
513 {
514     thread** ntid;
515
516     intsDisable();
517
518     /* A null tid means the current thread */
519     if (tid == 0)
520     {
521                 tid = currentThread;
522     }
523
524         DBG( printf("killThread %p\n", tid); );
525
526         if (CONTEXT(tid).status != THREAD_DEAD)
527         {
528                 /* Get thread off runq (if it needs it) */
529                 if (CONTEXT(tid).status == THREAD_RUNNING)
530                 {
531                         for (ntid = &threadQhead[CONTEXT(tid).priority];
532                                  *ntid != 0;
533                                  ntid = &(*ntid)->next)
534                         {
535                                 if (*ntid == tid)
536                                 {
537                                         *ntid = tid->next;
538                                         break;
539                                 }
540                         }
541                 }
542
543                 CONTEXT(tid).status = THREAD_DEAD;
544                 talive--;
545                 if (tid->daemon) {
546                         tdaemon--;
547                 }
548
549                 /* If we only have daemons left, then everyone is dead. */
550                 if (talive == tdaemon) {
551                         /* atexit functions get called to clean things up */
552                         intsRestore();
553                         exit(0);
554                 }
555
556                 /* Notify on the object just in case anyone is waiting */
557                 internal_lock_mutex_for_object(&tid->header);
558                 internal_broadcast_cond_for_object(&tid->header);
559                 internal_unlock_mutex_for_object(&tid->header);
560
561                 /* Remove thread from live list to it can be garbaged */
562                 for (ntid = &liveThreads;
563                          *ntid != 0;
564                          ntid = &(CONTEXT((*ntid)).nextlive))
565                 {
566                         if (tid == (*ntid))
567                         {
568                                 (*ntid) = CONTEXT(tid).nextlive;
569                                 break;
570                         }
571                 }
572
573                 /* Free stack */
574                 freeThreadStack(tid);
575
576                 /* free context */
577                 if (tid != mainThread) {
578                         CONTEXT(tid).free = true;
579                         CONTEXT(tid).thread = NULL;
580                 }
581
582                 /* Run something else */
583                 needReschedule = true;
584         }
585         intsRestore();
586 }
587
588 /*
589  * Change thread priority.
590  */
591 void
592 setPriorityThread(thread* tid, int prio)
593 {
594     thread** ntid;
595
596         assert(prio >= MIN_THREAD_PRIO && prio <= MAX_THREAD_PRIO);
597
598     if (tid->PrivateInfo == 0) {
599                 tid->priority = prio;
600                 return;
601     }
602
603     if (CONTEXT(tid).status == THREAD_SUSPENDED) {
604                 CONTEXT(tid).priority = (u8)prio;
605                 return;
606     }
607
608     intsDisable();
609
610     /* Remove from current thread list */
611     for (ntid = &threadQhead[CONTEXT(tid).priority]; *ntid != 0; ntid = &(*ntid)->next) {
612                 if (*ntid == tid) {
613                         *ntid = tid->next;
614                         break;
615                 }
616     }
617
618     /* Insert onto a new one */
619     tid->priority = prio;
620     CONTEXT(tid).priority = (u8)tid->priority;
621     if (threadQhead[prio] == 0) {
622                 threadQhead[prio] = tid;
623                 threadQtail[prio] = tid;
624                 if (prio > CONTEXT(currentThread).priority) {
625                         needReschedule = true;
626                 }
627     }
628     else {
629                 threadQtail[prio]->next = tid;
630                 threadQtail[prio] = tid;
631     }
632     tid->next = 0;
633
634     intsRestore();
635 }
636
637 /*
638  * Get the current time in milliseconds since 1970-01-01.
639  */
640 s8
641 currentTime (void)
642 {
643         struct timeval tv;
644         s8 time;
645
646         gettimeofday(&tv, 0);
647
648         time = tv.tv_sec;
649         time *= 1000;
650         time += tv.tv_usec / 1000;
651
652         return time;
653 }
654
655 /*
656  * Put a thread to sleep.
657  */
658 void
659 sleepThread (s8 time)
660 {
661     thread** tidp;
662
663     /* Sleep for no time */
664     if (time <= 0) {
665                 return;
666     }
667     
668     intsDisable();
669
670     /* Get absolute time */
671     CONTEXT(currentThread).time = time + currentTime();
672
673     /* Find place in alarm list */
674     for (tidp = &sleepThreads; (*tidp) != 0; tidp = &(*tidp)->next)
675         {
676                 if (CONTEXT(*tidp).time > CONTEXT(currentThread).time)
677                         break;
678     }
679
680     /* Suspend thread on it */
681     suspendOnQThread(currentThread, tidp);
682     
683     intsRestore();
684 }
685
686 /*
687  * Is this thread alive?
688  */
689 bool
690 aliveThread(thread* tid)
691 {
692     if (tid->PrivateInfo != 0 && CONTEXT(tid).status != THREAD_DEAD)
693                 return (true);
694     else
695                 return (false);
696 }
697
698 /*
699  * Reschedule the thread.
700  * Called whenever a change in the running thread is required.
701  */
702 void
703 reschedule(void)
704 {
705     int i;
706     thread* lastThread;
707     int b;
708     /*    sigset_t nsig; */
709
710     /* A reschedule in a non-blocked context is half way to hell */
711     assert(blockInts > 0);
712     b = blockInts;
713     
714     /* Check events - we may release a high priority thread */
715     /* Just check IO, no need for a reschedule call by checkEvents() */
716     needReschedule = false;
717     checkEvents(false);
718
719     for (;;)
720     {
721                 for (i = MAX_THREAD_PRIO; i >= MIN_THREAD_PRIO; i--)
722                 {
723                         if (threadQhead[i] != 0)
724                         {
725                                 DBG( fprintf(stderr, "found thread %p in head %d\n", threadQhead[i], i); );
726
727                                 if (threadQhead[i] != currentThread)
728                                 {
729                                         /* USEDSTACKTOP((CONTEXT(currentThread).usedStackTop)); */
730
731                                         lastThread = currentThread;
732                                         currentThread = threadQhead[i];
733
734                                         CONTEXT(currentThread).texceptionptr = *exceptionptr;
735
736                     DBG( fprintf(stderr, "thread switch from: %p to: %p\n", lastThread, currentThread); );
737                                         THREADSWITCH((&CONTEXT(currentThread)),
738                                                                  (&CONTEXT(lastThread)));
739                                         blockInts = b;
740
741                                         *exceptionptr = CONTEXT(currentThread).texceptionptr;
742
743                                         if (stack_to_be_freed != 0) {
744                                                 stack_to_be_freed = 0;
745                                         }
746
747                                         /* Alarm signal may be blocked - if so
748                                          * unblock it.
749                                          */
750                                         /*
751                                           if (alarmBlocked == true) {
752                                           alarmBlocked = false;
753                                           sigemptyset(&nsig);
754                                           sigaddset(&nsig, SIGALRM);
755                                           sigprocmask(SIG_UNBLOCK, &nsig, 0);
756                                           }
757                                         */
758
759                                         /* I might be dying */
760                                         if ((CONTEXT(lastThread).flags & THREAD_FLAGS_KILLED)
761                                                 != 0)
762                                         {
763                                                 CONTEXT(lastThread).flags &= ~THREAD_FLAGS_KILLED;
764                                                 *exceptionptr = native_new_and_init(class_java_lang_ThreadDeath);
765                                         }
766                                 }
767                                 /* Now we kill the schedule and turn ints
768                                    back on */
769                                 needReschedule = false;
770                                 return;
771                         }
772                 }
773                 /* Nothing to run - wait for external event */
774                 DBG( fprintf(stderr, "nothing more to do\n"); );
775                 checkEvents(true);
776     }
777 }
778
779 void lock_stopworld(int dummy)
780 {
781 }
782
783 void unlock_stopworld()
784 {
785 }
786
787 void cacao_suspendhandler(void *ctx)
788 {
789 }
790
791 #endif
792