@@ -132,7 +132,7 @@ pub fn get_cache_storage_dir() -> PathBuf {
132
132
pub fn create_isolate_create_params ( ) -> Option < v8:: CreateParams > {
133
133
#[ cfg( any( target_os = "android" , target_os = "linux" ) ) ]
134
134
{
135
- get_memory_limit_linux ( ) . map ( |memory_limit| {
135
+ linux :: get_memory_limit ( ) . map ( |memory_limit| {
136
136
v8:: CreateParams :: default ( )
137
137
. heap_limits_from_system_memory ( memory_limit, 0 )
138
138
} )
@@ -147,48 +147,129 @@ pub fn create_isolate_create_params() -> Option<v8::CreateParams> {
147
147
}
148
148
}
149
149
150
- /// Get memory limit with cgroup (either v1 or v2) taken into account.
151
150
#[ cfg( any( target_os = "android" , target_os = "linux" ) ) ]
152
- fn get_memory_limit_linux ( ) -> Option < u64 > {
153
- let system_total_memory =
154
- deno_runtime:: deno_os:: sys_info:: mem_info ( ) . map ( |mem_info| mem_info. total ) ;
155
-
156
- let Ok ( self_cgroup) = std:: fs:: read_to_string ( "/proc/self/cgroup" ) else {
157
- return system_total_memory;
158
- } ;
159
-
160
- let limit = match self_cgroup. strip_prefix ( "0::/" ) {
161
- Some ( cgroup_v2_relpath) => {
162
- // cgroup v2
163
- let limit_path = std:: path:: Path :: new ( "/sys/fs/cgroup" )
164
- . join ( cgroup_v2_relpath)
165
- . join ( "memory.max" ) ;
166
- std:: fs:: read_to_string ( limit_path)
167
- . ok ( )
168
- . and_then ( |s| s. trim ( ) . parse :: < u64 > ( ) . ok ( ) )
169
- }
170
- None => {
171
- // cgroup v1
172
- let Some ( cgroup_v1_relpath) = self_cgroup. lines ( ) . find_map ( |l| {
173
- let split = l. split ( ":" ) . collect :: < Vec < _ > > ( ) ;
174
- if split. get ( 1 ) == Some ( & "memory" ) {
175
- split. get ( 2 )
176
- } else {
177
- None
151
+ mod linux {
152
+ /// Get memory limit with cgroup (either v1 or v2) taken into account.
153
+ pub ( super ) fn get_memory_limit ( ) -> Option < u64 > {
154
+ let system_total_memory = deno_runtime:: deno_os:: sys_info:: mem_info ( )
155
+ . map ( |mem_info| mem_info. total ) ;
156
+
157
+ let Ok ( self_cgroup) = std:: fs:: read_to_string ( "/proc/self/cgroup" ) else {
158
+ return system_total_memory;
159
+ } ;
160
+
161
+ let limit = match parse_self_cgroup ( & self_cgroup) {
162
+ CgroupVersion :: V1 { cgroup_relpath } => {
163
+ let limit_path = std:: path:: Path :: new ( "/sys/fs/cgroup/memory" )
164
+ . join ( cgroup_relpath)
165
+ . join ( "memory.limit_in_bytes" ) ;
166
+ std:: fs:: read_to_string ( limit_path)
167
+ . ok ( )
168
+ . and_then ( |s| s. trim ( ) . parse :: < u64 > ( ) . ok ( ) )
169
+ }
170
+ CgroupVersion :: V2 { cgroup_relpath } => {
171
+ let limit_path = std:: path:: Path :: new ( "/sys/fs/cgroup" )
172
+ . join ( cgroup_relpath)
173
+ . join ( "memory.max" ) ;
174
+ std:: fs:: read_to_string ( limit_path)
175
+ . ok ( )
176
+ . and_then ( |s| s. trim ( ) . parse :: < u64 > ( ) . ok ( ) )
177
+ }
178
+ CgroupVersion :: None => system_total_memory,
179
+ } ;
180
+
181
+ limit. or ( system_total_memory)
182
+ }
183
+
184
+ enum CgroupVersion < ' a > {
185
+ V1 { cgroup_relpath : & ' a str } ,
186
+ V2 { cgroup_relpath : & ' a str } ,
187
+ None ,
188
+ }
189
+
190
+ fn parse_self_cgroup < ' a > ( self_cgroup_content : & ' a str ) -> CgroupVersion < ' a > {
191
+ let mut cgroup_version = CgroupVersion :: None ;
192
+
193
+ for line in self_cgroup_content. lines ( ) {
194
+ let split = line. split ( ":" ) . collect :: < Vec < _ > > ( ) ;
195
+
196
+ match & split[ ..] {
197
+ // A line like `4:memory:/foo/bar` means that memory is managed by v1.
198
+ [ _, "memory" , cgroup_v1_relpath] => {
199
+ cgroup_version = CgroupVersion :: V1 {
200
+ cgroup_relpath : cgroup_v1_relpath,
201
+ } ;
202
+ break ;
178
203
}
179
- } ) else {
180
- return system_total_memory;
181
- } ;
182
- let limit_path = std:: path:: Path :: new ( "/sys/fs/cgroup/memory" )
183
- . join ( cgroup_v1_relpath)
184
- . join ( "memory.limit_in_bytes" ) ;
185
- std:: fs:: read_to_string ( limit_path)
186
- . ok ( )
187
- . and_then ( |s| s. trim ( ) . parse :: < u64 > ( ) . ok ( ) )
204
+ // A line like `0::/foo/bar` means that cgroup v2 is used.
205
+ // However, it is still possible that memory is managed by v1 when
206
+ // hybrid mode is enabled. We continue until the whole lines are checked
207
+ // or an explicit memory line (indicating v1) is found.
208
+ [ "0" , "" , cgroup_v2_relpath] => {
209
+ cgroup_version = CgroupVersion :: V2 {
210
+ cgroup_relpath : cgroup_v2_relpath,
211
+ } ;
212
+ }
213
+ _ => { }
214
+ }
188
215
}
189
- } ;
190
216
191
- limit. or ( system_total_memory)
217
+ cgroup_version
218
+ }
219
+
220
+ #[ test]
221
+ fn test_parse_self_cgroup_v2 ( ) {
222
+ let self_cgroup = "0::/user.slice/user-1000.slice/session-3.scope" ;
223
+ let cgroup_version = parse_self_cgroup ( self_cgroup) ;
224
+ assert ! ( matches!(
225
+ cgroup_version,
226
+ CgroupVersion :: V2 { cgroup_relpath } if cgroup_relpath == "/user.slice/user-1000.slice/session-3.scope"
227
+ ) ) ;
228
+ }
229
+
230
+ #[ test]
231
+ fn test_parse_self_cgroup_hybrid ( ) {
232
+ let self_cgroup = r#"12:rdma:/
233
+ 11:blkio:/user.slice
234
+ 10:devices:/user.slice
235
+ 9:cpu,cpuacct:/user.slice
236
+ 8:pids:/user.slice/user-1000.slice/session-3.scope
237
+ 7:memory:/user.slice/user-1000.slice/session-3.scope
238
+ 6:perf_event:/
239
+ 5:freezer:/
240
+ 4:net_cls,net_prio:/
241
+ 3:hugetlb:/
242
+ 2:cpuset:/
243
+ 1:name=systemd:/user.slice/user-1000.slice/session-3.scope
244
+ 0::/user.slice/user-1000.slice/session-3.scope
245
+ "# ;
246
+ let cgroup_version = parse_self_cgroup ( self_cgroup) ;
247
+ assert ! ( matches!(
248
+ cgroup_version,
249
+ CgroupVersion :: V1 { cgroup_relpath } if cgroup_relpath == "/user.slice/user-1000.slice/session-3.scope"
250
+ ) ) ;
251
+ }
252
+
253
+ #[ test]
254
+ fn test_parse_self_cgroup_v1 ( ) {
255
+ let self_cgroup = r#"11:hugetlb:/
256
+ 10:pids:/user.slice/user-1000.slice
257
+ 9:perf_event:/
258
+ 8:devices:/user.slice
259
+ 7:net_cls,net_prio:/
260
+ 6:memory:/
261
+ 5:blkio:/
262
+ 4:cpuset:/
263
+ 3:cpu,cpuacct:/
264
+ 2:freezer:/
265
+ 1:name=systemd:/user.slice/user-1000.slice/session-2.scope
266
+ "# ;
267
+ let cgroup_version = parse_self_cgroup ( self_cgroup) ;
268
+ assert ! ( matches!(
269
+ cgroup_version,
270
+ CgroupVersion :: V1 { cgroup_relpath } if cgroup_relpath == "/"
271
+ ) ) ;
272
+ }
192
273
}
193
274
194
275
#[ derive( Debug , thiserror:: Error , deno_error:: JsError ) ]
0 commit comments