localStorage线程安全吗? [英] Is localStorage thread safe?

查看:132
本文介绍了localStorage线程安全吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我很好奇通过在两个浏览器标签中同时覆盖它来破坏 localStorage 条目的可能性。我应该为本地存储创建互斥吗?

我已经考虑过这样的伪类:

I'm curious about the possibility of damaging localStorage entry by overwriting it in two browser tabs simultaneously. Should I create a mutex for local storage?
I was already thinking of such pseudo-class:

LocalStorageMan.prototype.v = LocalStorageMan.prototype.value = function(name, val) {
  //Set inner value
  this.data[name] = val;
  //Delay any changes if the local storage is being changed
  if(localStorage[this.name+"__mutex"]==1) {
    setTimeout(function() {this.v(name, val);}, 1);
    return null;  //Very good point @Lightness Races in Orbit 
  }
  //Lock the mutext to prevent overwriting
  localStorage[this.name+"__mutex"] = 1;
  //Save serialized data
  localStorage[this.name] = this.serializeData;
  //Allow usage from another tabs
  localStorage[this.name+"__mutex"] = 0;
}

上述函数意味着本地存储管理器正在管理本地的一个特定密钥存储 - localStorage [test] 例如。我想将此用于greasomonkey用户脚本,其中避免冲突是一个优先事项。

The function above implies local storage manager that is managing one specific key of the local storage - localStorage["test"] for example. I want to use this for greasomonkey userscripts where avoiding conlicts is a priority.

推荐答案

是的,它是线程安全的。但是,您的代码不是原子的,那是你的问题。我将解决 localStorage 的安全问题,但首先是如何解决问题。

Yes, it is thread safe. However, your code isn't atomic and that's your problem there. I'll get to thread safety of localStorage but first, how to fix your problem.

两个标签都可以通过如果一起检查 并写入相互覆盖的项目。处理此问题的正确方法是使用 StorageEvent 秒。

Both tabs can pass the if check together and write to the item overwriting each other. The correct way to handle this problem is using StorageEvents.

这些允许您在localStorage中更改密钥时通知其他窗口,从而以安全的方式在内置消息中有效地解决问题。 以下是关于它们的精彩内容。让我们举个例子:

These let you notify other windows when a key has changed in localStorage, effectively solving the problem for you in a built in message passing safe way. Here is a nice read about them. Let's give an example:

// tab 1
localStorage.setItem("Foo","Bar");

// tab 2
window.addEventListener("storage",function(e){
    alert("StorageChanged!"); // this will run when the localStorage is changed
});






现在,我承诺线程安全:)


Now, what I promised about thread safety :)

我喜欢 - 让我们从两个角度来看 - 从规范和使用实现。

As I like - let's observe this from two angles - from the specification and using the implementation.

让我们按照规范显示它的线程安全。

Let's show it's thread safe by specification.

如果我们检查网络存储规范,我们可以看到它具体说明

If we check the specification of Web Storage we can see that it specifically notes:


由于使用了存储互斥锁,多个浏览上下文将能够以脚本无法检测到任何并发脚本执行的方式同时访问本地存储区域。

Because of the use of the storage mutex, multiple browsing contexts will be able to access the local storage areas simultaneously in such a manner that scripts cannot detect any concurrent script execution.

因此,存储对象的length属性以及该对象的各种属性的值在脚本执行时不能改变,而不是以脚本本身可预测的方式。

Thus, the length attribute of a Storage object, and the value of the various properties of that object, cannot change while a script is executing, other than in a way that is predictable by the script itself.

它甚至进一步阐述:


每当属性一个 localStorage 属性的 S. torage 对象将被检查,返回,设置或删除,无论是作为直接属性访问的一部分,还是在检查属性的存在时,在属性枚举期间,在确定存在的属性数时或者作为执行存储接口上定义的任何方法或属性的一部分,用户代理必须首先获取存储互斥

Whenever the properties of a localStorage attribute's Storage object are to be examined, returned, set, or deleted, whether as part of a direct property access, when checking for the presence of a property, during property enumeration, when determining the number of properties present, or as part of the execution of any of the methods or attributes defined on the Storage interface, the user agent must first obtain the storage mutex.

强调我的。它还指出,一些实现者并不喜欢这样的说明。

Emphasis mine. It also notes that some implementors don't like this as a note.

让我们在实现中展示它的线程安全。

Let's show it's thread safe in implementation.

选择一个随机浏览器,我选择了WebKit(因为我之前不知道那个代码在哪里)。如果我们检查WebKit的存储的实现,我们可以看出它有互斥量的票价份额。

Choosing a random browser, I chose WebKit (because I didn't know where that code is located there before). If we check at WebKit's implementation of Storage we can see that it has its fare share of mutexes.

让我们从头开始。当您调用 setItem 或分配时,会发生这种情况:

Let's take it from the start. When you call setItem or assign, this happens:

void Storage::setItem(const String& key, const String& value, ExceptionCode& ec)
{
    if (!m_storageArea->canAccessStorage(m_frame)) {
        ec = SECURITY_ERR;
        return;
    }

    if (isDisabledByPrivateBrowsing()) {
        ec = QUOTA_EXCEEDED_ERR;
        return;
    }

    bool quotaException = false;
    m_storageArea->setItem(m_frame, key, value, quotaException);

    if (quotaException)
        ec = QUOTA_EXCEEDED_ERR;
}

接下来,这发生在 StorageArea

void StorageAreaImpl::setItem(Frame* sourceFrame, const String& key, const String& value, bool& quotaException)
{
    ASSERT(!m_isShutdown);
    ASSERT(!value.isNull());
    blockUntilImportComplete();

    String oldValue;
    RefPtr<StorageMap> newMap = m_storageMap->setItem(key, value, oldValue, quotaException);
    if (newMap)
        m_storageMap = newMap.release();

    if (quotaException)
        return;

    if (oldValue == value)
        return;

    if (m_storageAreaSync)
        m_storageAreaSync->scheduleItemForSync(key, value);

    dispatchStorageEvent(key, oldValue, value, sourceFrame);
}

请注意 blockUntilImportComplete 这里。让我们来看看:

Note that blockUntilImportComplete here. Let's look at that:

void StorageAreaSync::blockUntilImportComplete()
{
    ASSERT(isMainThread());

    // Fast path.  We set m_storageArea to 0 only after m_importComplete being true.
    if (!m_storageArea)
        return;

    MutexLocker locker(m_importLock);
    while (!m_importComplete)
        m_importCondition.wait(m_importLock);
    m_storageArea = 0;
}

他们还添加了一个不错的笔记:

They also went as far as add a nice note:

// FIXME: In the future, we should allow use of StorageAreas while it's importing (when safe to do so).
// Blocking everything until the import is complete is by far the simplest and safest thing to do, but
// there is certainly room for safe optimization: Key/length will never be able to make use of such an
// optimization (since the order of iteration can change as items are being added). Get can return any
// item currently in the map. Get/remove can work whether or not it's in the map, but we'll need a list
// of items the import should not overwrite. Clear can also work, but it'll need to kill the import
// job first.

解释这个有效,但效率更高。

Explaining this works, but it can be more efficient.

这篇关于localStorage线程安全吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆