|
2 | 2 | use crate::exceptions; |
3 | 3 | use crate::function::{Args, KwArgs, OptionalArg, PyFuncArgs}; |
4 | 4 | use crate::obj::objdict::PyDictRef; |
| 5 | +use crate::obj::objstr::PyStringRef; |
5 | 6 | use crate::obj::objtuple::PyTupleRef; |
6 | 7 | use crate::obj::objtype::PyClassRef; |
7 | 8 | use crate::pyobject::{ |
8 | | - Either, IdProtocol, PyCallable, PyClassImpl, PyObjectRef, PyRef, PyResult, PyValue, |
9 | | - TypeProtocol, |
| 9 | + Either, IdProtocol, ItemProtocol, PyCallable, PyClassImpl, PyObjectRef, PyRef, PyResult, |
| 10 | + PyValue, TypeProtocol, |
10 | 11 | }; |
11 | 12 | use crate::vm::VirtualMachine; |
12 | 13 |
|
| 14 | +use crossbeam_utils::atomic::AtomicCell; |
13 | 15 | use parking_lot::{ |
14 | 16 | lock_api::{RawMutex as RawMutexT, RawMutexTimed, RawReentrantMutex}, |
15 | 17 | RawMutex, RawThreadId, |
16 | 18 | }; |
17 | 19 | use std::cell::RefCell; |
| 20 | +use std::collections::HashMap; |
18 | 21 | use std::io::Write; |
19 | 22 | use std::time::Duration; |
20 | 23 | use std::{fmt, thread}; |
@@ -264,12 +267,99 @@ fn thread_count(vm: &VirtualMachine) -> usize { |
264 | 267 | vm.state.thread_count.load() |
265 | 268 | } |
266 | 269 |
|
| 270 | +static LOCAL_DATA_KEY: AtomicCell<usize> = AtomicCell::new(0); |
| 271 | +thread_local!(static LOCAL_DATA: RefCell<HashMap<usize, PyDictRef>> = RefCell::default()); |
| 272 | + |
| 273 | +#[pyclass(name = "_local")] |
| 274 | +#[derive(Debug)] |
| 275 | +struct PyLocal { |
| 276 | + key: usize, |
| 277 | +} |
| 278 | + |
| 279 | +impl PyValue for PyLocal { |
| 280 | + fn class(vm: &VirtualMachine) -> PyClassRef { |
| 281 | + vm.class("_thread", "_local") |
| 282 | + } |
| 283 | +} |
| 284 | + |
| 285 | +impl Drop for PyLocal { |
| 286 | + fn drop(&mut self) { |
| 287 | + LOCAL_DATA.with(|map| map.borrow_mut().remove(&self.key)); |
| 288 | + } |
| 289 | +} |
| 290 | + |
| 291 | +#[pyimpl(flags(BASETYPE))] |
| 292 | +impl PyLocal { |
| 293 | + fn ldict(&self, vm: &VirtualMachine) -> PyDictRef { |
| 294 | + LOCAL_DATA.with(|map| { |
| 295 | + map.borrow_mut() |
| 296 | + .entry(self.key) |
| 297 | + .or_insert_with(|| vm.ctx.new_dict()) |
| 298 | + .clone() |
| 299 | + }) |
| 300 | + } |
| 301 | + |
| 302 | + #[pyslot] |
| 303 | + fn tp_new(cls: PyClassRef, _args: PyFuncArgs, vm: &VirtualMachine) -> PyResult<PyRef<Self>> { |
| 304 | + PyLocal { |
| 305 | + key: LOCAL_DATA_KEY.fetch_add(1), |
| 306 | + } |
| 307 | + .into_ref_with_type(vm, cls) |
| 308 | + } |
| 309 | + |
| 310 | + #[pymethod(magic)] |
| 311 | + fn getattribute(zelf: PyRef<Self>, attr: PyStringRef, vm: &VirtualMachine) -> PyResult { |
| 312 | + let ldict = zelf.ldict(vm); |
| 313 | + if attr.as_str() == "__dict__" { |
| 314 | + Ok(ldict.into_object()) |
| 315 | + } else { |
| 316 | + let zelf = zelf.into_object(); |
| 317 | + vm.generic_getattribute_opt(zelf.clone(), attr.clone(), Some(ldict))? |
| 318 | + .ok_or_else(|| { |
| 319 | + vm.new_attribute_error(format!("{} has no attribute '{}'", zelf, attr)) |
| 320 | + }) |
| 321 | + } |
| 322 | + } |
| 323 | + |
| 324 | + #[pymethod(magic)] |
| 325 | + fn setattr( |
| 326 | + zelf: PyRef<Self>, |
| 327 | + attr: PyStringRef, |
| 328 | + value: PyObjectRef, |
| 329 | + vm: &VirtualMachine, |
| 330 | + ) -> PyResult<()> { |
| 331 | + if attr.as_str() == "__dict__" { |
| 332 | + Err(vm.new_attribute_error(format!( |
| 333 | + "{} attribute '__dict__' is read-only", |
| 334 | + zelf.as_object() |
| 335 | + ))) |
| 336 | + } else { |
| 337 | + zelf.ldict(vm).set_item(attr.as_object(), value, vm)?; |
| 338 | + Ok(()) |
| 339 | + } |
| 340 | + } |
| 341 | + |
| 342 | + #[pymethod(magic)] |
| 343 | + fn delattr(zelf: PyRef<Self>, attr: PyStringRef, vm: &VirtualMachine) -> PyResult<()> { |
| 344 | + if attr.as_str() == "__dict__" { |
| 345 | + Err(vm.new_attribute_error(format!( |
| 346 | + "{} attribute '__dict__' is read-only", |
| 347 | + zelf.as_object() |
| 348 | + ))) |
| 349 | + } else { |
| 350 | + zelf.ldict(vm).del_item(attr.as_object(), vm)?; |
| 351 | + Ok(()) |
| 352 | + } |
| 353 | + } |
| 354 | +} |
| 355 | + |
267 | 356 | pub fn make_module(vm: &VirtualMachine) -> PyObjectRef { |
268 | 357 | let ctx = &vm.ctx; |
269 | 358 |
|
270 | 359 | py_module!(vm, "_thread", { |
271 | 360 | "RLock" => PyRLock::make_class(ctx), |
272 | 361 | "LockType" => PyLock::make_class(ctx), |
| 362 | + "_local" => PyLocal::make_class(ctx), |
273 | 363 | "get_ident" => ctx.new_function(thread_get_ident), |
274 | 364 | "allocate_lock" => ctx.new_function(thread_allocate_lock), |
275 | 365 | "start_new_thread" => ctx.new_function(thread_start_new_thread), |
|
0 commit comments