File: StreamConnectionBuffer.h

package info (click to toggle)
webkit2gtk 2.48.5-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 429,764 kB
  • sloc: cpp: 3,697,587; javascript: 194,444; ansic: 169,997; python: 46,499; asm: 19,295; ruby: 18,528; perl: 16,602; xml: 4,650; yacc: 2,360; sh: 2,098; java: 1,993; lex: 1,327; pascal: 366; makefile: 298
file content (147 lines) | stat: -rw-r--r-- 7,464 bytes parent folder | download | duplicates (7)
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
147
/*
 * Copyright (C) 2020 Apple Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 */

#pragma once

#include <WebCore/SharedMemory.h>
#include <cstddef>
#include <span>
#include <wtf/Atomics.h>
#include <wtf/Ref.h>
#include <wtf/StdLibExtras.h>

namespace IPC {
class Decoder;
class Encoder;

// StreamConnectionBuffer is a shared "bi-partite" circular buffer supporting variable length messages, specific data
// alignment with mandated minimum size. StreamClientConnection and StreamServerConnection use StreamConnectionBuffer to
// communicate.
//
// The "bi-partite" is here to mean that writes and reads to the buffer must be continuous.
//
// The caller (client or server) "acquires" some amount of buffer data. The data pointer location depends on the amount
// of data being acquired. The buffer returns all the available capacity -- this may be less than acquired.
//
// The caller "releases" the some amount of data, at least the amount acquired but potentially more.
//
// Both the client and the server must release the same amount of data at the same step of the acquire/release sequence.
// Suppose the client releases first 8 and then 166 bytes. Upon the first acquire, the server must also release 8 bytes
// and then upon the second acquire 166 bytes. Due to how alignment and minimum length affect the position at which the
// memory can be referred to, the server cannot "read two times simultaneously" and release 174 bytes.
//
// The buffer also supports synchronous replies via "releaseAll/acquireAll" call pattern. The communication protocol can
// establish cases where the client transfers ownership of the data to the server. In these cases the server will
// acknowledge the read with "releaseAll" operation. The client will then read the reply via "acquireAll" operation. The
// reply will be written to the beginning of the buffer. This mandates that in-place data references in the buffer
// cannot be used simultaneously for message data and the reply data. In current IPC implementation, the message
// processing uses the in-place data references, while the reply data is first constructed to allocated memory and then
// copied to the message buffer.
//
// The circular buffer has following implementation:
// * The client owns the data between [clientOffset, serverOffset[. When clientOffset == serverOffset, the client owns
//   all the data.
// * The server owns the data between [serverOffset, clientOffset[.
// * The exception to the above is when communication protocol can contain messages that denote that the server will own
//   all the data.
// * The buffer can hold maximum of size - 1 values. The last value is reserved for indicating that the buffer is full.
//   FIXME: Maybe would be simpler implementation if it would use the "wrap" flag instead of the hole as the indicator.
//   This would move the alignedSpan implementation to the StreamConnectionBuffer.
// * All atomic variable loads are untrusted, so they're clamped. Violations are not reported, though.
class StreamConnectionBuffer {
    WTF_MAKE_NONCOPYABLE(StreamConnectionBuffer);
public:
    ~StreamConnectionBuffer();

    using Handle = WebCore::SharedMemory::Handle;
    Handle createHandle();

    size_t wrapOffset(size_t offset) const
    {
        ASSERT(offset <= dataSize());
        if (offset >= dataSize())
            return 0;
        return offset;
    }

    template<size_t messageAlignment>
    size_t alignOffset(size_t offset, size_t acquireSize) const
    {
        offset = roundUpToMultipleOf<messageAlignment>(offset);
        if (offset + acquireSize >= dataSize())
            offset = 0;
        return offset;
    }

    // Value denoting the client index to the buffer, with special tag values.
    // The client trusts this. The server converts the value to trusted size_t offset with a special function.
    enum ClientOffset : size_t {
        // Tag value stored in when the server is sleeping, e.g. not running.
        serverIsSleepingTag = 1u << 31
    };

    // Value denoting the server index to the buffer, with special tag values.
    // The client trusts this. The server converts the value to trusted size_t offset with a special function.
    enum ServerOffset : size_t {
        // Tag value stored when the client is waiting, e.g. waiting on a semaphore.
        clientIsWaitingTag = 1u << 31
    };

    Atomic<ClientOffset>& clientOffset() { return header().clientOffset; }
    Atomic<ServerOffset>& serverOffset() { return header().serverOffset; }
    std::span<const uint8_t> span() const { return m_sharedMemory->mutableSpan().subspan(headerSize()); }
    std::span<uint8_t> mutableSpan() { return m_sharedMemory->mutableSpan().subspan(headerSize()); }
    size_t dataSize() const { return m_dataSize; }

    static constexpr size_t maximumSize() { return std::min(static_cast<size_t>(ClientOffset::serverIsSleepingTag), static_cast<size_t>(ClientOffset::serverIsSleepingTag)) - 1; }

    std::span<uint8_t> headerForTesting();
    std::span<uint8_t> dataForTesting() { return mutableSpan(); }

    static constexpr bool sharedMemorySizeIsValid(size_t size) { return headerSize() < size && size <= headerSize() + maximumSize(); }

protected:
    StreamConnectionBuffer(Ref<WebCore::SharedMemory>&&);
    StreamConnectionBuffer(StreamConnectionBuffer&&) = default;
    StreamConnectionBuffer& operator=(StreamConnectionBuffer&&) = default;

    struct Header {
        Atomic<ServerOffset> serverOffset;
        // Padding so that the variables mostly accessed by different processes do not share a cache line.
        // This is an attempt to avoid cache-line induced reduction of parallel access.
        // Use 128 bytes since that's the cache line size on ARM64, and enough to cover other platforms where 64 bytes is common.
        alignas(128) Atomic<ClientOffset> clientOffset;
    };

#undef HEADER_POINTER_ALIGNMENT

    Header& header() const { return reinterpretCastSpanStartTo<Header>(m_sharedMemory->mutableSpan()); }
    static constexpr size_t headerSize() { return roundUpToMultipleOf<alignof(std::max_align_t)>(sizeof(Header)); }

    size_t m_dataSize { 0 };
    Ref<WebCore::SharedMemory> m_sharedMemory;
};

}