Clipboard: perform non-blocking reads from compositor

Since the compositor recycles file descriptors when offering data to
clients, it is possible that a client (or multiple clients) can attempt
to read from the same FD simultaneously, creating a situation where one
read can wait indefinitely for data. By making the pipe non-blocking,
the client can gracefully time-out if data doesn't arrive in a timely
manner.

This commit also ensures that only one FD is open per client per offered
mime type.

Change-Id: If052e5bf8198df1cdc8fc4df360f83a77950b2f9
Reviewed-by: Robin Burchell <robin+qt@viroteck.net>
This commit is contained in:
Andrew Knight 2013-10-21 17:45:11 +03:00 committed by The Qt Project
parent 47d979b9fa
commit def782f1a4
2 changed files with 51 additions and 22 deletions

View File

@ -84,7 +84,6 @@ QWaylandMimeData::QWaylandMimeData(QWaylandDataOffer *dataOffer, QWaylandDisplay
: QInternalMimeData()
, m_dataOffer(dataOffer)
, m_display(display)
, m_offered_mime_types()
{
}
@ -94,46 +93,73 @@ QWaylandMimeData::~QWaylandMimeData()
void QWaylandMimeData::appendFormat(const QString &mimeType)
{
m_offered_mime_types.append(mimeType);
if (m_types.contains(mimeType))
close(m_types.take(mimeType)); // Unconsumed data
m_data.remove(mimeType); // Clear previous contents
int pipefd[2];
if (::pipe2(pipefd, O_CLOEXEC|O_NONBLOCK) == -1) {
qWarning("QWaylandMimeData: pipe2() failed");
return;
}
m_dataOffer->receive(mimeType, pipefd[1]);
m_display->forceRoundTrip();
close(pipefd[1]);
m_types.insert(mimeType, pipefd[0]);
}
bool QWaylandMimeData::hasFormat_sys(const QString &mimeType) const
{
return m_offered_mime_types.contains(mimeType);
return m_types.contains(mimeType) || m_data.contains(mimeType);
}
QStringList QWaylandMimeData::formats_sys() const
{
return m_offered_mime_types;
return m_types.keys() << m_data.keys();
}
QVariant QWaylandMimeData::retrieveData_sys(const QString &mimeType, QVariant::Type type) const
{
Q_UNUSED(type);
if (m_offered_mime_types.isEmpty())
if (m_data.contains(mimeType))
return m_data.value(mimeType);
if (!m_types.contains(mimeType))
return QVariant();
int pipefd[2];
if (qt_safe_pipe(pipefd, O_CLOEXEC) == -1) {
qWarning("QWaylandMimeData: pipe() failed");
return QVariant();
}
m_dataOffer->receive(mimeType, pipefd[1]);
close(pipefd[1]);
// m_display->forceRoundTrip();
QByteArray content;
char buf[4096];
int n;
while ((n = QT_READ(pipefd[0], &buf, sizeof buf)) > 0) {
content.append(buf, n);
int fd = m_types.take(mimeType);
if (readData(fd, content) != 0) {
qWarning("QWaylandDataOffer: error reading data for mimeType %s", qPrintable(mimeType));
content = QByteArray();
}
close(pipefd[0]);
close(fd);
m_data.insert(mimeType, content);
return content;
}
int QWaylandMimeData::readData(int fd, QByteArray &data) const
{
char buf[4096];
int retryCount = 0;
int n;
while (true) {
n = QT_READ(fd, buf, sizeof buf);
if (n == -1 && (errno == EAGAIN || errno == EWOULDBLOCK) && ++retryCount < 1000)
usleep(1000);
else
break;
}
if (retryCount >= 1000)
qWarning("QWaylandDataOffer: timeout reading from pipe");
if (n > 0) {
data.append(buf, n);
n = readData(fd, data);
}
return n;
}
QT_END_NAMESPACE

View File

@ -82,9 +82,12 @@ protected:
QVariant retrieveData_sys(const QString &mimeType, QVariant::Type type) const Q_DECL_OVERRIDE;
private:
int readData(int fd, QByteArray &data) const;
mutable QWaylandDataOffer *m_dataOffer;
QWaylandDisplay *m_display;
QStringList m_offered_mime_types;
mutable QHash<QString, int> m_types;
mutable QHash<QString, QByteArray> m_data;
};
QT_END_NAMESPACE