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
|
#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), mNumAnswerStackFrame(0) {
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(
"_ipdltest::TestHangsParent::CleanUp", this, &TestHangsParent::CleanUp));
GetIPCChannel()->CloseWithTimeout();
return false;
}
mozilla::ipc::IPCResult TestHangsParent::AnswerStackFrame() {
++mNumAnswerStackFrame;
if (mNumAnswerStackFrame == 1) {
if (CallStackFrame()) {
fail("should have timed out!");
}
} else if (mNumAnswerStackFrame == 2) {
// 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!");
} else {
fail("unexpected state");
}
return IPC_OK();
}
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); }
mozilla::ipc::IPCResult 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 IPC_OK();
}
} // namespace _ipdltest
} // namespace mozilla
|