summaryrefslogtreecommitdiffstats
path: root/ipc/ipdl/test/cxx/TestHangs.cpp
blob: b96823aee3ae29e6692902405e171717e36702b6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
#include "base/process_util.h"

#include "TestHangs.h"

#include "IPDLUnitTests.h"      // fail etc.

using base::KillProcess;

namespace mozilla {
namespace _ipdltest {

//-----------------------------------------------------------------------------
// parent

TestHangsParent::TestHangsParent() : mDetectedHang(false)
{
    MOZ_COUNT_CTOR(TestHangsParent);
}

TestHangsParent::~TestHangsParent()
{
    MOZ_COUNT_DTOR(TestHangsParent);
}

void
TestHangsParent::Main()
{
    // Here we try to set things up to test the following sequence of events:
    //
    // - subprocess causes an OnMaybeDequeueOne() task to be posted to
    //   this thread
    //
    // - subprocess hangs just long enough for the hang timer to expire
    //
    // - hang-kill code in the parent starts running
    //
    // - subprocess replies to message while hang code runs
    //
    // - reply is processed in OnMaybeDequeueOne() before Close() has
    //   been called or the channel error notification has been posted

    // this tells the subprocess to send us Nonce()
    if (!SendStart())
        fail("sending Start");

    // now we sleep here for a while awaiting the Nonce() message from
    // the child.  since we're not blocked on anything, the IO thread
    // will enqueue an OnMaybeDequeueOne() task to process that
    // message
    // 
    // NB: PR_Sleep is exactly what we want, only the current thread
    // sleeping
    PR_Sleep(5000);

    // when we call into this, we'll pull the Nonce() message out of
    // the mPending queue, but that doesn't matter ... the
    // OnMaybeDequeueOne() event will remain
    if (CallStackFrame() && mDetectedHang)
        fail("should have timed out!");

    // the Close() task in the queue will shut us down
}

bool
TestHangsParent::ShouldContinueFromReplyTimeout()
{
    mDetectedHang = true;

    // so we've detected a timeout after 2 ms ... now we cheat and
    // sleep for a long time, to allow the subprocess's reply to come
    // in

    PR_Sleep(5000);

    // reply should be here; we'll post a task to shut things down.
    // This must be after OnMaybeDequeueOne() in the event queue.
    MessageLoop::current()->PostTask(
        NewNonOwningRunnableMethod(this, &TestHangsParent::CleanUp));

    GetIPCChannel()->CloseWithTimeout();

    return false;
}

bool
TestHangsParent::AnswerStackFrame()
{
    if (PTestHangs::HANG != state()) {
        if (CallStackFrame())
            fail("should have timed out!");
    }
    else {
        // minimum possible, 2 ms.  We want to detecting a hang to race
        // with the reply coming in, as reliably as possible
        SetReplyTimeoutMs(2);

        if (CallHang())
            fail("should have timed out!");
    }

    return true;
}

void
TestHangsParent::CleanUp()
{
    ipc::ScopedProcessHandle otherProcessHandle;
    if (!base::OpenProcessHandle(OtherPid(), &otherProcessHandle.rwget())) {
        fail("couldn't open child process");
    } else {
        if (!KillProcess(otherProcessHandle, 0, false)) {
            fail("terminating child process");
        }
    }
    Close();
}


//-----------------------------------------------------------------------------
// child

TestHangsChild::TestHangsChild()
{
    MOZ_COUNT_CTOR(TestHangsChild);
}

TestHangsChild::~TestHangsChild()
{
    MOZ_COUNT_DTOR(TestHangsChild);
}

bool
TestHangsChild::AnswerHang()
{
    puts(" (child process is 'hanging' now)");

    // just sleep until we're reasonably confident the 1ms hang
    // detector fired in the parent process and it's sleeping in
    // ShouldContinueFromReplyTimeout()
    PR_Sleep(1000);

    return true;
}

} // namespace _ipdltest
} // namespace mozilla