1/*
2 * Copyright (C) 2007 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#import "config.h"
27#import "ThreadCheck.h"
28
29#import <wtf/HashSet.h>
30#import <wtf/StdLibExtras.h>
31#include <wtf/text/StringHash.h>
32
33namespace WebCore {
34
35static bool didReadThreadViolationBehaviorFromUserDefaults = false;
36static bool threadViolationBehaviorIsDefault = true;
37static ThreadViolationBehavior threadViolationBehavior[MaximumThreadViolationRound] = { RaiseExceptionOnThreadViolation, RaiseExceptionOnThreadViolation };
38
39static void readThreadViolationBehaviorFromUserDefaults()
40{
41    didReadThreadViolationBehaviorFromUserDefaults = true;
42
43    ThreadViolationBehavior newBehavior = LogOnFirstThreadViolation;
44    NSString *threadCheckLevel = [[NSUserDefaults standardUserDefaults] stringForKey:@"WebCoreThreadCheck"];
45    if (!threadCheckLevel)
46        return;
47
48    if ([threadCheckLevel isEqualToString:@"None"])
49        newBehavior = NoThreadCheck;
50    else if ([threadCheckLevel isEqualToString:@"Exception"])
51        newBehavior = RaiseExceptionOnThreadViolation;
52    else if ([threadCheckLevel isEqualToString:@"Log"])
53        newBehavior = LogOnThreadViolation;
54    else if ([threadCheckLevel isEqualToString:@"LogOnce"])
55        newBehavior = LogOnFirstThreadViolation;
56    else
57        ASSERT_NOT_REACHED();
58
59    threadViolationBehaviorIsDefault = false;
60
61    for (unsigned i = 0; i < MaximumThreadViolationRound; ++i)
62        threadViolationBehavior[i] = newBehavior;
63}
64
65void setDefaultThreadViolationBehavior(ThreadViolationBehavior behavior, ThreadViolationRound round)
66{
67    ASSERT(round < MaximumThreadViolationRound);
68    if (round >= MaximumThreadViolationRound)
69        return;
70    if (!didReadThreadViolationBehaviorFromUserDefaults)
71        readThreadViolationBehaviorFromUserDefaults();
72    if (threadViolationBehaviorIsDefault)
73        threadViolationBehavior[round] = behavior;
74}
75
76void reportThreadViolation(const char* function, ThreadViolationRound round)
77{
78    ASSERT(round < MaximumThreadViolationRound);
79    if (round >= MaximumThreadViolationRound)
80        return;
81    if (!didReadThreadViolationBehaviorFromUserDefaults)
82        readThreadViolationBehaviorFromUserDefaults();
83    if (threadViolationBehavior[round] == NoThreadCheck)
84        return;
85    if (pthread_main_np())
86        return;
87    WebCoreReportThreadViolation(function, round);
88}
89
90} // namespace WebCore
91
92// Split out the actual reporting of the thread violation to make it easier to set a breakpoint
93void WebCoreReportThreadViolation(const char* function, WebCore::ThreadViolationRound round)
94{
95    using namespace WebCore;
96
97    ASSERT(round < MaximumThreadViolationRound);
98    if (round >= MaximumThreadViolationRound)
99        return;
100
101    DEPRECATED_DEFINE_STATIC_LOCAL(HashSet<String>, loggedFunctions, ());
102    switch (threadViolationBehavior[round]) {
103        case NoThreadCheck:
104            break;
105        case LogOnFirstThreadViolation:
106            if (loggedFunctions.add(function).isNewEntry) {
107                NSLog(@"WebKit Threading Violation - %s called from secondary thread", function);
108                NSLog(@"Additional threading violations for this function will not be logged.");
109            }
110            break;
111        case LogOnThreadViolation:
112            NSLog(@"WebKit Threading Violation - %s called from secondary thread", function);
113            break;
114        case RaiseExceptionOnThreadViolation:
115            [NSException raise:@"WebKitThreadingException" format:@"%s was called from a secondary thread", function];
116            break;
117    }
118}
119