summaryrefslogtreecommitdiffstats
path: root/ipc/ipdl/test/cxx/TestInterruptShutdownRace.cpp
blob: 38f7993da736fbf22085c2da44aeeaf4000e2234 (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
#include "TestInterruptShutdownRace.h"

#include "base/task.h"
#include "IPDLUnitTests.h"      // fail etc.
#include "IPDLUnitTestSubprocess.h"

namespace mozilla {
namespace _ipdltest {

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

namespace {

// NB: this test does its own shutdown, rather than going through
// QuitParent(), because it's testing degenerate edge cases

void DeleteSubprocess()
{
    delete gSubprocess;
    gSubprocess = nullptr;
}

void Done()
{
    passed(__FILE__);
    QuitParent();
}

} // namespace <anon>

TestInterruptShutdownRaceParent::TestInterruptShutdownRaceParent()
{
    MOZ_COUNT_CTOR(TestInterruptShutdownRaceParent);
}

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

void
TestInterruptShutdownRaceParent::Main()
{
    if (!SendStart())
        fail("sending Start");
}

bool
TestInterruptShutdownRaceParent::RecvStartDeath()
{
    // this will be ordered before the OnMaybeDequeueOne event of
    // Orphan in the queue
    MessageLoop::current()->PostTask(
        NewNonOwningRunnableMethod(this,
				   &TestInterruptShutdownRaceParent::StartShuttingDown));
    return true;
}

void
TestInterruptShutdownRaceParent::StartShuttingDown()
{
    // NB: we sleep here to try and avoid receiving the Orphan message
    // while waiting for the CallExit() reply.  if we fail at that, it
    // will cause the test to pass spuriously, because there won't be
    // an OnMaybeDequeueOne task for Orphan
    PR_Sleep(2000);

    if (CallExit())
        fail("connection was supposed to be interrupted");

    Close();

    delete static_cast<TestInterruptShutdownRaceParent*>(gParentActor);
    gParentActor = nullptr;

    XRE_GetIOMessageLoop()->PostTask(NewRunnableFunction(DeleteSubprocess));

    // this is ordered after the OnMaybeDequeueOne event in the queue
    MessageLoop::current()->PostTask(NewRunnableFunction(Done));

    // |this| has been deleted, be mindful
}

bool
TestInterruptShutdownRaceParent::RecvOrphan()
{
    // it would be nice to fail() here, but we'll process this message
    // while waiting for the reply CallExit().  The OnMaybeDequeueOne
    // task will still be in the queue, it just wouldn't have had any
    // work to do, if we hadn't deleted ourself
    return true;
}

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

TestInterruptShutdownRaceChild::TestInterruptShutdownRaceChild()
{
    MOZ_COUNT_CTOR(TestInterruptShutdownRaceChild);
}

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

bool
TestInterruptShutdownRaceChild::RecvStart()
{
    if (!SendStartDeath())
        fail("sending StartDeath");

    // See comment in StartShuttingDown(): we want to send Orphan()
    // while the parent is in its PR_Sleep()
    PR_Sleep(1000);

    if (!SendOrphan())
        fail("sending Orphan");

    return true;
}

bool
TestInterruptShutdownRaceChild::AnswerExit()
{
    _exit(0);
    NS_RUNTIMEABORT("unreached");
    return false;
}


} // namespace _ipdltest
} // namespace mozilla