[Libreoffice-commits] online.git: Mobile/TestFakeSocket net/FakeSocket.cpp
Libreoffice Gerrit user
logerrit at kemper.freedesktop.org
Sun Sep 16 07:42:36 UTC 2018
Mobile/TestFakeSocket/TestFakeSocket/main.cpp | 111 +++++++++++-
net/FakeSocket.cpp | 228 ++++++++++++++++++++------
2 files changed, 274 insertions(+), 65 deletions(-)
New commits:
commit 30ec64566b87cafc2fb192bf0e550a23e5a31f52
Author: Tor Lillqvist <tml at iki.fi>
AuthorDate: Sat Sep 15 23:29:39 2018 +0300
Commit: Tor Lillqvist <tml at collabora.com>
CommitDate: Sun Sep 16 10:42:01 2018 +0300
Attempt to fix various FakeSocket problems
Also add a bit of internals documentation.
diff --git a/Mobile/TestFakeSocket/TestFakeSocket/main.cpp b/Mobile/TestFakeSocket/TestFakeSocket/main.cpp
index 22e46d85c..2e18cd8f1 100644
--- a/Mobile/TestFakeSocket/TestFakeSocket/main.cpp
+++ b/Mobile/TestFakeSocket/TestFakeSocket/main.cpp
@@ -20,7 +20,7 @@ int main(int argc, char **argv)
int s1 = fakeSocketSocket();
int s2 = fakeSocketSocket();
- std::cout << "sockets: " << s0 << ", " << s1 << ", " << s2 << "\n";
+ std::cout << "sockets: s0=" << s0 << ", s1=" << s1 << ", s2=" << s2 << "\n";
fakeSocketClose(s1);
@@ -30,15 +30,26 @@ int main(int argc, char **argv)
int rc = fakeSocketListen(s0);
if (rc == -1)
{
- perror("listen");
+ perror("listening on s0");
return 1;
}
- int s3;
+ int s3, s4;
std::thread t0([&] {
s3 = fakeSocketAccept4(s0, 0);
if (s3 == -1)
+ {
perror("accept");
+ return;
+ }
+ std::cout << "accepted s3=" << s3 << " from s0\n";
+ s4 = fakeSocketAccept4(s0, 0);
+ if (s4 == -1)
+ {
+ perror("accept");
+ return;
+ }
+ std::cout << "accepted s4=" << s4 << " from s0\n";
});
rc = fakeSocketConnect(s1, s0);
@@ -47,20 +58,38 @@ int main(int argc, char **argv)
perror("connect");
return 1;
}
+ std::cout << "connected s1\n";
+
+ rc = fakeSocketConnect(s2, s0);
+ if (rc == -1)
+ {
+ perror("connect");
+ return 1;
+ }
+ std::cout << "connected s2\n";
t0.join();
- if (s3 == -1)
+ if (s3 == -1 || s4 == -1)
return 1;
- rc = fakeSocketWrite(s1, "hello", 6);
+ rc = fakeSocketWrite(s1, "hello", 5);
if (rc == -1)
{
perror("write");
return 1;
}
- std::cout << "wrote 'hello'\n";
+ std::cout << "wrote 'hello' to s1\n";
+
+ rc = fakeSocketWrite(s2, "moin", 4);
+ if (rc == -1)
+ {
+ perror("write");
+ return 1;
+ }
+ std::cout << "wrote 'moin' to s2\n";
char buf[100];
+
rc = fakeSocketRead(s3, buf, 100);
if (rc == -1)
{
@@ -68,31 +97,40 @@ int main(int argc, char **argv)
return 1;
}
buf[rc] = 0;
- std::cout << "read " << buf << "\n";
+ std::cout << "read " << buf << " from s3\n";
- rc = fakeSocketWrite(s1, "goodbye", 7);
+ rc = fakeSocketRead(s4, buf, 100);
+ if (rc == -1)
+ {
+ perror("read");
+ return 1;
+ }
+ buf[rc] = 0;
+ std::cout << "read '" << buf << "' from s4\n";
+
+ rc = fakeSocketWrite(s3, "goodbye", 7);
if (rc == -1)
{
perror("write");
return 1;
}
- std::cout << "wrote 'goodbye'\n";
+ std::cout << "wrote 'goodbye' to s3\n";
- rc = fakeSocketRead(s3, buf, 4);
+ rc = fakeSocketRead(s1, buf, 4);
if (rc != -1)
{
std::cerr << "Tried partial read, and succeeded!?\n";
return 1;
}
- rc = fakeSocketRead(s3, buf, 100);
+ rc = fakeSocketRead(s1, buf, 100);
if (rc == -1)
{
perror("read");
return 1;
}
buf[rc] = 0;
- std::cout << "read " << buf << "\n";
+ std::cout << "read '" << buf << "' from s1\n";
int pipe[2];
rc = fakeSocketPipe2(pipe);
@@ -102,6 +140,35 @@ int main(int argc, char **argv)
return 1;
}
+ fakeSocketClose(s3);
+ std::cout << "closed s3\n";
+
+ rc = fakeSocketRead(s1, buf, 100);
+ if (rc == -1)
+ {
+ perror("read");
+ return 1;
+ }
+ if (rc != 0)
+ {
+ std::cerr << "read '" << buf << "' from s1 after peer s3 was closed!?\n";
+ return 1;
+ }
+ std::cout << "correctly got eof from s1\n";
+
+ rc = fakeSocketRead(s1, buf, 100);
+ if (rc == -1)
+ {
+ perror("read");
+ return 1;
+ }
+ if (rc != 0)
+ {
+ std::cerr << "read '" << buf << "' from s1 after peer s3 was closed!?\n";
+ return 1;
+ }
+ std::cout << "correctly got eof from s1\n";
+
rc = fakeSocketWrite(pipe[0], "x", 1);
if (rc == -1)
{
@@ -116,7 +183,25 @@ int main(int argc, char **argv)
}
if (buf[0] != 'x')
{
- std::cerr << "Wrote 'x' but read '" << buf[0] << "'\n";
+ std::cerr << "wrote 'x' to pipe but read '" << buf[0] << "'\n";
+ return 1;
+ }
+
+ rc = fakeSocketWrite(pipe[1], "y", 1);
+ if (rc == -1)
+ {
+ perror("write");
+ return 1;
+ }
+ rc = fakeSocketRead(pipe[0], buf, 1);
+ if (rc == -1)
+ {
+ perror("read");
+ return 1;
+ }
+ if (buf[0] != 'y')
+ {
+ std::cerr << "wrote 'y' to pipe but read '" << buf[0] << "'\n";
return 1;
}
diff --git a/net/FakeSocket.cpp b/net/FakeSocket.cpp
index e9681b392..43107ef3e 100644
--- a/net/FakeSocket.cpp
+++ b/net/FakeSocket.cpp
@@ -19,14 +19,33 @@
#include "FakeSocket.hpp"
+// A "fake socket" is represented by a number, a smallish integer, just like a real socket.
+//
+// There is one FakeSocketPair for each two sequential fake socket numbers. When you create one, you
+// will always get the lower (even) number in a pair. The higher number wil be returned if you
+// sucessfully call fakeSocketConnect() from the lower number to some other fake socket.
+//
+// After you create a fake socket, there is basically just two things you can do with it:
+//
+// 1) Call fakeSocketConnect on it giving another fake socket number to connect to. Once the
+// connection is successful, you can call fakeSocketRead() and fakeSocketWrite() on your original
+// socket.
+//
+// 2) Call fakeSocketListen() on it, indicating it is a "server" socket. After that, keep calling
+// fakeSocketAccept() and each time that returns successfully, it will return a new fake socket that
+// is connected to another fake socket that called fakeSocketConnect() to the server socket. You can
+// then call fakeSocketRead() and fakeSocketWrite() on it.
+//
+// This all is complicated a bit by the fact that all the API is non-blocking.
+
struct FakeSocketPair
{
int fd[2];
bool listening;
int connectingFd;
+ bool readable[2];
std::vector<char> buffer[2];
std::mutex *mutex;
- // std::condition_variable *cv;
FakeSocketPair()
{
@@ -34,12 +53,14 @@ struct FakeSocketPair
fd[1] = -1;
listening = false;
connectingFd = -1;
+ readable[0] = false;
+ readable[1] = false;
mutex = new std::mutex();
- // cv = new std::condition_variable();
}
};
static std::mutex fdsMutex;
+static std::mutex cvMutex;
static std::condition_variable cv;
// Avoid problems with order of initialisation of static globals.
@@ -53,17 +74,16 @@ static std::vector<FakeSocketPair>& getFds()
int fakeSocketSocket()
{
std::vector<FakeSocketPair>& fds = getFds();
- std::cerr << "----- &fds=" << &fds << " size=" << fds.size() << std::endl;
std::lock_guard<std::mutex> fdsLock(fdsMutex);
- size_t i;
- for (i = 0; i < fds.size(); i++)
- {
- if (fds[i].fd[0] == -1 && fds[i].fd[1] == -1)
- break;
- }
- if (i == fds.size())
- fds.resize(fds.size() + 1);
+
+ // We always allocate a new FakeSocketPair struct. Let's not bother with potential issues with
+ // reusing them. It isn't like we would be allocating thousands anyway during the typical
+ // lifetime of an app.
+
+ const int i = fds.size();
+ fds.resize(i + 1);
+
FakeSocketPair& result = fds[i];
result.fd[0] = i*2;
@@ -98,32 +118,97 @@ int fakeSocketPipe2(int pipefd[2])
return 0;
}
-static bool someFdReadable(struct pollfd *pollfds, int nfds)
+static std::string pollBits(int bits)
+{
+ if (bits == 0)
+ return "-";
+
+ std::string result;
+
+ if (bits & POLLERR)
+ {
+ if (result != "")
+ result += "+";
+ result += "ERR";
+ }
+ if (bits & POLLHUP)
+ {
+ if (result != "")
+ result += "+";
+ result += "HUP";
+ }
+ if (bits & POLLIN)
+ {
+ if (result != "")
+ result += "+";
+ result += "IN";
+ }
+ if (bits & POLLNVAL)
+ {
+ if (result != "")
+ result += "+";
+ result += "NVAL";
+ }
+ if (bits & POLLOUT)
+ {
+ if (result != "")
+ result += "+";
+ result += "OUT";
+ }
+ if (bits & POLLPRI)
+ {
+ if (result != "")
+ result += "+";
+ result += "PRI";
+ }
+
+ return result;
+}
+
+static bool checkForPoll(std::vector<FakeSocketPair>& fds, struct pollfd *pollfds, int nfds)
{
- std::vector<FakeSocketPair>& fds = getFds();
bool retval = false;
for (int i = 0; i < nfds; i++)
{
- pollfds[i].revents = 0;
- const int K = ((pollfds[i].fd)&1);
- if (pollfds[i].events & POLLIN)
- if (fds[pollfds[i].fd/2].fd[K] != -1 && fds[pollfds[i].fd/2].buffer[K].size() > 0)
+ // Caller sets POLLNVAL for invalid fds.
+ if (pollfds[i].revents != POLLNVAL)
+ {
+ pollfds[i].revents = 0;
+ const int K = ((pollfds[i].fd)&1);
+ const int N = 1 - K;
+ if (pollfds[i].events & POLLIN)
{
- pollfds[i].revents = POLLIN;
- retval = true;
+ if (fds[pollfds[i].fd/2].fd[K] != -1 &&
+ (fds[pollfds[i].fd/2].readable[K] ||
+ (K == 0 && fds[pollfds[i].fd/2].listening && fds[pollfds[i].fd/2].connectingFd != -1)))
+ {
+ pollfds[i].revents |= POLLIN;
+ retval = true;
+ }
}
+ // With our trivial single-message buffering, a socket is writable if the peer socket is
+ // open and not readable.
+ if (pollfds[i].events & POLLOUT)
+ {
+ if (fds[pollfds[i].fd/2].fd[N] != -1 && !fds[pollfds[i].fd/2].readable[N])
+ {
+ pollfds[i].revents |= POLLOUT;
+ retval = true;
+ }
+ }
+ }
}
return retval;
}
int fakeSocketPoll(struct pollfd *pollfds, int nfds, int timeout)
{
- std::cerr << "+++++ Polling " << nfds << " fds: ";
+ std::cerr << "+++++ Poll ";
for (int i = 0; i < nfds; i++)
{
if (i > 0)
std::cerr << ",";
- std::cerr << pollfds[i].fd;
+ std::cerr << pollfds[i].fd << ":" << pollBits(pollfds[i].events);
}
std::cerr << "\n";
@@ -131,18 +216,34 @@ int fakeSocketPoll(struct pollfd *pollfds, int nfds, int timeout)
std::unique_lock<std::mutex> fdsLock(fdsMutex);
for (int i = 0; i < nfds; i++)
{
- if (pollfds[i].fd < 1 || pollfds[i].fd/2 >= fds.size())
+ if (pollfds[i].fd < 0 || pollfds[i].fd/2 >= fds.size())
{
- errno = EBADF;
- return -1;
+ pollfds[i].revents = POLLNVAL;
+ }
+ else
+ {
+ const int K = ((pollfds[i].fd)&1);
+ if (fds[pollfds[i].fd/2].fd[K] == -1)
+ pollfds[i].revents = POLLNVAL;
+ else
+ pollfds[i].revents = 0;
}
}
- // Here we lock just the first FakeSocketPair struct, hmm
- std::unique_lock<std::mutex> fdLock(fds[pollfds[0].fd/2].mutex[0]);
+
+ std::unique_lock<std::mutex> cvLock(cvMutex);
fdsLock.unlock();
- while (!someFdReadable(pollfds, nfds))
- cv.wait(fdLock);
+ while (!checkForPoll(fds, pollfds, nfds))
+ cv.wait(cvLock);
+
+ std::cerr << "+++++ Poll result: ";
+ for (int i = 0; i < nfds; i++)
+ {
+ if (i > 0)
+ std::cerr << ",";
+ std::cerr << pollfds[i].fd << ":" << pollBits(pollfds[i].revents);
+ }
+ std::cerr << "\n";
return 0;
}
@@ -151,7 +252,7 @@ int fakeSocketListen(int fd)
{
std::vector<FakeSocketPair>& fds = getFds();
std::unique_lock<std::mutex> fdsLock(fdsMutex);
- if (fd < 0 || fd/2 >= fds.size())
+ if (fd < 0 || fd/2 >= fds.size() || fds[fd/2].fd[fd&1] == -1)
{
std::cerr << "+++++ EBADF: Listening on fd " << fd << "\n";
errno = EBADF;
@@ -223,13 +324,14 @@ int fakeSocketConnect(int fd1, int fd2)
}
pair2.connectingFd = fd1;
- // pair2.cv->notify_all();
cv.notify_all();
+ std::unique_lock<std::mutex> cvLock(cvMutex);
fdLock2.unlock();
+ fdLock1.unlock();
+
while (pair1.fd[1] == -1)
- // pair1.cv->wait(fdLock1);
- cv.wait(fdLock1);
+ cv.wait(cvLock);
assert(pair1.fd[1] == pair1.fd[0] + 1);
@@ -268,30 +370,30 @@ int fakeSocketAccept4(int fd, int flags)
std::unique_lock<std::mutex> fdLock(pair.mutex[0]);
fdsLock.unlock();
+ std::unique_lock<std::mutex> cvLock(cvMutex);
+ fdLock.unlock();
+
while (pair.connectingFd == -1)
- // pair.cv->wait(fdLock);
- cv.wait(fdLock);
+ cv.wait(cvLock);
assert(pair.connectingFd >= 0 && pair.connectingFd/2 < fds.size());
- FakeSocketPair& pair1 = fds[pair.connectingFd/2];
+ FakeSocketPair& pair2 = fds[pair.connectingFd/2];
- std::unique_lock<std::mutex> fdLock1(pair1.mutex[0]);
+ std::unique_lock<std::mutex> fdLock1(pair2.mutex[0]);
- assert(pair1.fd[1] == -1);
- assert(pair1.fd[0] == pair.connectingFd);
+ assert(pair2.fd[1] == -1);
+ assert(pair2.fd[0] == pair.connectingFd);
pair.connectingFd = -1;
- fdLock.unlock();
- pair1.fd[1] = pair1.fd[0] + 1;
+ pair2.fd[1] = pair2.fd[0] + 1;
- // pair1.cv->notify_one();
cv.notify_one();
- std::cerr << "+++++ Accept fd " << fd << ": " << pair1.fd[1] << "\n";
+ std::cerr << "+++++ Accept fd " << fd << ": " << pair2.fd[1] << "\n";
- return pair1.fd[1];
+ return pair2.fd[1];
}
int fakeSocketPeer(int fd)
@@ -333,6 +435,15 @@ ssize_t fakeSocketAvailableDataLength(int fd)
// K: for this fd
const int K = (fd&1);
+ if (!pair.readable[K])
+ {
+ std::cerr << "+++++ EAGAIN: Available data on fd " << fd << "\n";
+ errno = EAGAIN;
+ return -1;
+ }
+
+ std::cerr << "+++++ Available data on fd " << fd << ": " << pair.buffer[K].size() << "\n";
+
return pair.buffer[K].size();
}
@@ -354,6 +465,8 @@ ssize_t fakeSocketRead(int fd, void *buf, size_t nbytes)
// K: for this fd
const int K = (fd&1);
+ // N: for its peer
+ const int N = 1 - K;
if (pair.fd[K] == -1)
{
@@ -362,14 +475,14 @@ ssize_t fakeSocketRead(int fd, void *buf, size_t nbytes)
return -1;
}
- if (pair.buffer[K].size() == 0)
+ if (!pair.readable[K])
{
std::cerr << "+++++ EAGAIN: Read from fd " << fd << ", " << nbytes << (nbytes == 1 ? " byte" : " bytes") << "\n";
errno = EAGAIN;
return -1;
}
- // These sockets are record-oriented!
+ // These sockets are record-oriented! It won't work to read less than the whole buffer.
ssize_t result = pair.buffer[K].size();
if (nbytes < result)
{
@@ -380,8 +493,12 @@ ssize_t fakeSocketRead(int fd, void *buf, size_t nbytes)
memmove(buf, pair.buffer[K].data(), result);
pair.buffer[K].resize(0);
+ // If peer is closed, we continue to be readable
+ if (pair.fd[N] == -1)
+ pair.readable[K] = true;
+ else
+ pair.readable[K] = false;
- // pair.cv->notify_one();
cv.notify_one();
std::cerr << "+++++ Read from fd " << fd << ": " << result << (result == 1 ? " byte" : " bytes") << "\n";
@@ -415,7 +532,7 @@ ssize_t fakeSocketFeed(int fd, const void *buf, size_t nbytes)
return -1;
}
- if (pair.buffer[K].size() != 0)
+ if (pair.readable[K])
{
std::cerr << "+++++ EAGAIN: Feed to fd " << fd << ", " << nbytes << (nbytes == 1 ? " byte" : " bytes") << "\n";
errno = EAGAIN;
@@ -424,8 +541,8 @@ ssize_t fakeSocketFeed(int fd, const void *buf, size_t nbytes)
pair.buffer[K].resize(nbytes);
memmove(pair.buffer[K].data(), buf, nbytes);
+ pair.readable[K] = true;
- // pair.cv->notify_one();
cv.notify_one();
std::cerr << "+++++ Feed to fd " << fd << ": " << nbytes << (nbytes == 1 ? " byte" : " bytes") << "\n";
@@ -461,7 +578,7 @@ ssize_t fakeSocketWrite(int fd, const void *buf, size_t nbytes)
return -1;
}
- if (pair.buffer[N].size() != 0)
+ if (pair.readable[N])
{
std::cerr << "+++++ EAGAIN: Write to fd " << fd << ", " << nbytes << (nbytes == 1 ? " byte" : " bytes") << "\n";
errno = EAGAIN;
@@ -470,8 +587,8 @@ ssize_t fakeSocketWrite(int fd, const void *buf, size_t nbytes)
pair.buffer[N].resize(nbytes);
memmove(pair.buffer[N].data(), buf, nbytes);
+ pair.readable[N] = true;
- // pair.cv->notify_one();
cv.notify_one();
std::cerr << "+++++ Write to fd " << fd << ": " << nbytes << (nbytes == 1 ? " byte" : " bytes") << "\n";
@@ -494,9 +611,16 @@ int fakeSocketClose(int fd)
std::unique_lock<std::mutex> fdLock(pair.mutex[0]);
fdsLock.unlock();
- assert(pair.fd[fd&1] == fd);
+ const int K = (fd&1);
+ const int N = 1 - K;
+
+ assert(pair.fd[K] == fd);
- pair.fd[fd&1] = -1;
+ pair.fd[K] = -1;
+ pair.buffer[K].resize(0);
+ pair.readable[N] = true;
+
+ cv.notify_one();
std::cerr << "+++++ Close fd " << fd << "\n";
More information about the Libreoffice-commits
mailing list