#include <assert.h>
#include <errno.h>
#include <fcntl.h>
+#include <poll.h>
+#include <pthread.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
uint32_t *object_ids;
int nobject_ids;
+
+ /* for lease transfer completion */
+ uint32_t crtc_id;
+ pthread_t transition_tid;
+ bool transition_running;
};
struct lm {
return true;
}
+/* Lease transition
+ * Wait for a client to update the DRM framebuffer on the CRTC managed by
+ * a lease. Once the framebuffer has been updated, it is safe to close
+ * the fd associated with the previous lease client, freeing the previous
+ * framebuffer if there are no other references to it. */
+static void wait_for_fb_update(struct lease *lease, uint32_t old_fb)
+{
+ uint32_t current_fb = old_fb;
+
+ struct pollfd drm_poll = {
+ .fd = lease->lease_fd,
+ .events = POLLIN,
+ };
+
+ while (current_fb == old_fb) {
+ drmModeCrtcPtr crtc;
+ if (poll(&drm_poll, 1, -1) < 0) {
+ if (errno == EINTR)
+ continue;
+ break;
+ }
+
+ crtc = drmModeGetCrtc(lease->lease_fd, lease->crtc_id);
+ current_fb = crtc->buffer_id;
+ drmModeFreeCrtc(crtc);
+ }
+}
+
+struct transition_ctx {
+ struct lease *lease;
+ int close_fd;
+ uint32_t old_fb;
+};
+
+static void transition_done(void *arg)
+{
+ struct transition_ctx *ctx = arg;
+ close(ctx->close_fd);
+ free(ctx);
+}
+
+static void *finish_transition_task(void *arg)
+{
+ struct transition_ctx *ctx = arg;
+ pthread_cleanup_push(transition_done, ctx);
+ wait_for_fb_update(ctx->lease, ctx->old_fb);
+ pthread_cleanup_pop(true);
+ return NULL;
+}
+
+static void close_after_lease_transition(struct lease *lease, int close_fd)
+{
+ struct transition_ctx *ctx = calloc(1, sizeof(*ctx));
+
+ assert(ctx);
+
+ drmModeCrtcPtr crtc = drmModeGetCrtc(lease->lease_fd, lease->crtc_id);
+
+ ctx->lease = lease;
+ ctx->close_fd = close_fd;
+ ctx->old_fb = crtc->buffer_id;
+
+ drmModeFreeCrtc(crtc);
+
+ int ret = pthread_create(&lease->transition_tid, NULL,
+ finish_transition_task, ctx);
+
+ lease->transition_running = (ret == 0);
+}
+
+static void cancel_lease_transition_thread(struct lease *lease)
+{
+
+ if (lease->transition_running) {
+ pthread_cancel(lease->transition_tid);
+ pthread_join(lease->transition_tid, NULL);
+ }
+
+ lease->transition_running = false;
+}
+
static void lease_free(struct lease *lease)
{
free(lease->base.name);
goto err;
uint32_t crtc_id = lm->drm_resource->crtcs[crtc_index];
+ lease->crtc_id = crtc_id;
lease->object_ids[lease->nobject_ids++] = crtc_id;
lease->object_ids[lease->nobject_ids++] = connector->connector_id;
lease->is_granted = false;
+ lease->lease_fd = -1;
return lease;
assert(lm);
for (int i = 0; i < lm->nleases; i++) {
- lm_lease_revoke(lm, (struct lease_handle *)lm->leases[i]);
+ struct lease_handle *lease_handle = &lm->leases[i]->base;
+ lm_lease_revoke(lm, lease_handle);
+ lm_lease_close(lease_handle);
lease_free(lm->leases[i]);
}
return -1;
}
- lease->lease_fd =
+ int lease_fd =
drmModeCreateLease(lm->drm_fd, lease->object_ids,
lease->nobject_ids, 0, &lease->lessee_id);
- if (lease->lease_fd < 0) {
+ if (lease_fd < 0) {
ERROR_LOG("drmModeCreateLease failed on lease %s: %s\n",
lease->base.name, strerror(errno));
return -1;
}
lease->is_granted = true;
- return lease->lease_fd;
+
+ int old_lease_fd = lease->lease_fd;
+ lease->lease_fd = lease_fd;
+
+ if (old_lease_fd >= 0)
+ close_after_lease_transition(lease, old_lease_fd);
+
+ return lease_fd;
}
int lm_lease_transfer(struct lm *lm, struct lease_handle *handle)
if (!lease->is_granted)
return -1;
- // TODO: close this fd once a frame is presented from the new
- // client.
- int old_lease_fd = dup(lease->lease_fd);
-
lm_lease_revoke(lm, handle);
if (lm_lease_grant(lm, handle) < 0) {
- close(old_lease_fd);
+ lm_lease_close(handle);
return -1;
}
return;
drmModeRevokeLease(lm->drm_fd, lease->lessee_id);
- close(lease->lease_fd);
+ cancel_lease_transition_thread(lease);
lease->is_granted = false;
}
+
+void lm_lease_close(struct lease_handle *handle)
+{
+ assert(handle);
+
+ struct lease *lease = (struct lease *)handle;
+ close(lease->lease_fd);
+ lease->lease_fd = -1;
+}