Coverage for /home/antoine/projects/xpra-git/dist/python3/lib64/python/xpra/client/mixins/mmap.py : 80%
Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# This file is part of Xpra.
2# Copyright (C) 2010-2020 Antoine Martin <antoine@xpra.org>
3# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
4# later version. See the file COPYING for details.
6import os
8from xpra.util import envbool, typedict
9from xpra.exit_codes import EXIT_MMAP_TOKEN_FAILURE
10from xpra.scripts.config import TRUE_OPTIONS
11from xpra.simple_stats import std_unit
12from xpra.client.mixins.stub_client_mixin import StubClientMixin
13from xpra.log import Logger
15log = Logger("mmap")
17KEEP_MMAP_FILE = envbool("XPRA_KEEP_MMAP_FILE", False)
20"""
21Mixin for adding mmap support to a client
22"""
23class MmapClient(StubClientMixin):
25 def __init__(self):
26 StubClientMixin.__init__(self)
27 self.mmap_enabled = False
28 self.mmap = None
29 self.mmap_token = None
30 self.mmap_token_index = 0
31 self.mmap_token_bytes = 0
32 self.mmap_filename = None
33 self.mmap_size = 0
34 self.mmap_group = None
35 self.mmap_tempfile = None
36 self.mmap_delete = False
37 self.supports_mmap = True
40 def init(self, opts):
41 self.mmap_group = opts.mmap_group
42 if os.path.isabs(opts.mmap):
43 self.mmap_filename = opts.mmap
44 self.supports_mmap = True
45 else:
46 self.supports_mmap = opts.mmap.lower() in TRUE_OPTIONS
49 def cleanup(self):
50 self.clean_mmap()
53 def setup_connection(self, conn):
54 if self.supports_mmap:
55 self.init_mmap(self.mmap_filename, self.mmap_group, conn.filename)
58 def get_root_size(self):
59 #subclasses should provide real values
60 return 1024, 1024
62 def parse_server_capabilities(self, c : typedict) -> bool:
63 self.mmap_enabled = self.supports_mmap and self.mmap_enabled and c.boolget("mmap_enabled")
64 log("parse_server_capabilities(..) mmap_enabled=%s", self.mmap_enabled)
65 if self.mmap_enabled:
66 from xpra.net.mmap_pipe import read_mmap_token, DEFAULT_TOKEN_INDEX, DEFAULT_TOKEN_BYTES
67 def iget(attrname, default_value=0):
68 return c.intget("mmap_%s" % attrname) or c.intget("mmap.%s" % attrname) or default_value
69 mmap_token = iget("token")
70 mmap_token_index = iget("token_index", DEFAULT_TOKEN_INDEX)
71 mmap_token_bytes = iget("token_bytes", DEFAULT_TOKEN_BYTES)
72 token = read_mmap_token(self.mmap, mmap_token_index, mmap_token_bytes)
73 if token!=mmap_token:
74 log.error("Error: mmap token verification failed!")
75 log.error(" expected '%#x'", token)
76 log.error(" found '%#x'", mmap_token)
77 self.mmap_enabled = False
78 self.quit(EXIT_MMAP_TOKEN_FAILURE)
79 return
80 log.info("enabled fast mmap transfers using %sB shared memory area", std_unit(self.mmap_size, unit=1024))
81 #the server will have a handle on the mmap file by now, safe to delete:
82 if not KEEP_MMAP_FILE:
83 self.clean_mmap()
84 return True
87 def get_info(self):
88 if not self.mmap_enabled:
89 return {}
90 mmap_info = self.get_raw_caps()
91 mmap_info["group"] = self.mmap_group or ""
92 return {
93 "mmap" : mmap_info,
94 }
96 def get_caps(self) -> dict:
97 if not self.mmap_enabled:
98 return {}
99 raw_caps = self.get_raw_caps()
100 caps = {
101 "mmap" : raw_caps,
102 }
103 #pre 2.3 servers only use underscore instead of "." prefix for mmap caps:
104 for k,v in raw_caps.items():
105 caps["mmap_%s" % k] = v
106 return caps
108 def get_raw_caps(self):
109 return {
110 "file" : self.mmap_filename,
111 "size" : self.mmap_size,
112 "token" : self.mmap_token,
113 "token_index" : self.mmap_token_index,
114 "token_bytes" : self.mmap_token_bytes,
115 "namespace" : True, #this client understands "mmap.ATTRIBUTE" format
116 }
118 def init_mmap(self, mmap_filename, mmap_group, socket_filename):
119 log("init_mmap(%s, %s, %s)", mmap_filename, mmap_group, socket_filename)
120 from xpra.os_util import get_int_uuid
121 from xpra.net.mmap_pipe import init_client_mmap, write_mmap_token, DEFAULT_TOKEN_INDEX, DEFAULT_TOKEN_BYTES
122 #calculate size:
123 root_w, root_h = self.get_root_size()
124 #at least 256MB, or 8 fullscreen RGBX frames:
125 mmap_size = max(256*1024*1024, root_w*root_h*4*8)
126 mmap_size = min(1024*1024*1024, mmap_size)
127 self.mmap_enabled, self.mmap_delete, self.mmap, self.mmap_size, self.mmap_tempfile, self.mmap_filename = \
128 init_client_mmap(mmap_group, socket_filename, mmap_size, self.mmap_filename)
129 if self.mmap_enabled:
130 self.mmap_token = get_int_uuid()
131 self.mmap_token_bytes = DEFAULT_TOKEN_BYTES
132 self.mmap_token_index = self.mmap_size - DEFAULT_TOKEN_BYTES
133 #self.mmap_token_index = DEFAULT_TOKEN_INDEX*2
134 #write the token twice:
135 # once at the old default offset for older servers,
136 # and at the offset we want to use with new servers
137 for index in (DEFAULT_TOKEN_INDEX, self.mmap_token_index):
138 write_mmap_token(self.mmap, self.mmap_token, index, self.mmap_token_bytes)
140 def clean_mmap(self):
141 log("XpraClient.clean_mmap() mmap_filename=%s", self.mmap_filename)
142 if self.mmap_tempfile:
143 try:
144 self.mmap_tempfile.close()
145 except Exception as e:
146 log("clean_mmap error closing file %s: %s", self.mmap_tempfile, e)
147 self.mmap_tempfile = None
148 if self.mmap_delete:
149 #this should be redundant: closing the tempfile should get it deleted
150 if self.mmap_filename and os.path.exists(self.mmap_filename):
151 from xpra.net.mmap_pipe import clean_mmap
152 clean_mmap(self.mmap_filename)
153 self.mmap_filename = None