************************* G4 reminder *************************
These new files:

        
c:\src-gears3\googleclient\gears\opensource\gears\base\common\message_queue_cr.cc

are missing unit tests.
***************************************************************

Hello mpcomplete,

I'd like you to do a code review.  Please execute
        g4 diff -c 8312735

or point your web browser to
        http://mondrian/8312735

to review the following code:

Change 8312735 by [EMAIL PROTECTED] on 2008/09/18 11:26:38 *pending*

        Adding a message queue for chrome that doesn't require a windows 
message loop to be running on the main thread.
        
        R=mpcomplete
        [EMAIL PROTECTED]
        DELTA=252  (250 added, 0 deleted, 2 changed)
        OCL=8312735

Affected files ...

... //depot/googleclient/gears/opensource/gears/Makefile#183 edit
... 
//depot/googleclient/gears/opensource/gears/base/common/message_queue_cr.cc#1 
add
... 
//depot/googleclient/gears/opensource/third_party/chrome/chrome_plugin_api.h#2 
edit

252 delta lines: 250 added, 0 deleted, 2 changed

Also consider running:
        g4 lint -c 8312735

which verifies that the changelist doesn't introduce new style violations.

If you can't do the review, please let me know as soon as possible.  During
your review, please ensure that all new code has corresponding unit tests and
that existing unit tests are updated appropriately.  Visit
http://www/eng/code_review.html for more information.

This is a semiautomated message from "g4 mail".  Complaints or suggestions?
Mail [EMAIL PROTECTED]
Change 8312735 by [EMAIL PROTECTED] on 2008/09/18 11:26:38 *pending*

        Adding a message queue for chrome that doesn't require a windows 
message loop to be running on the main thread.

Affected files ...

... //depot/googleclient/gears/opensource/gears/Makefile#183 edit
... 
//depot/googleclient/gears/opensource/gears/base/common/message_queue_cr.cc#1 
add
... 
//depot/googleclient/gears/opensource/third_party/chrome/chrome_plugin_api.h#2 
edit

==== //depot/googleclient/gears/opensource/gears/Makefile#183 - 
c:\src-gears3/googleclient/gears/opensource/gears/Makefile ====
# action=edit type=text
--- googleclient/gears/opensource/gears/Makefile        2008-09-17 
14:36:00.000000000 -0700
+++ googleclient/gears/opensource/gears/Makefile        2008-09-16 
11:30:25.000000000 -0700
@@ -1106,7 +1106,7 @@
                detect_version_collision_win32.cc \
                file_win32.cc \
                ie_version.cc \
-               message_queue_ie.cc \
+               message_queue_cr.cc \
                time_utils_win32.cc \
                vista_utils.cc \
                $(NULL)
==== 
//depot/googleclient/gears/opensource/gears/base/common/message_queue_cr.cc#1 - 
c:\src-gears3/googleclient/gears/opensource/gears/base/common/message_queue_cr.cc
 ====
# action=add type=text
--- /dev/null   1969-12-31 16:00:00.000000000 -0800
+++ googleclient/gears/opensource/gears/base/common/message_queue_cr.cc 
2008-09-18 11:28:45.000000000 -0700
@@ -0,0 +1,245 @@
+// Copyright 2007, Google Inc.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+//  1. Redistributions of source code must retain the above copyright notice,
+//     this list of conditions and the following disclaimer.
+//  2. Redistributions in binary form must reproduce the above copyright 
notice,
+//     this list of conditions and the following disclaimer in the 
documentation
+//     and/or other materials provided with the distribution.
+//  3. Neither the name of Google Inc. nor the names of its contributors may be
+//     used to endorse or promote products derived from this software without
+//     specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <assert.h>
+#include <deque>
+#include <map>
+#include <string>
+#include <windows.h>
+
+#include "gears/base/common/atomic_ops.h"
+#include "gears/base/common/message_queue.h"
+#include "gears/base/chrome/module_cr.h"
+#include "gears/base/common/thread_locals.h"
+#include "third_party/linked_ptr/linked_ptr.h"
+#include "third_party/scoped_ptr/scoped_ptr.h"
+
+
+static const ThreadLocals::Slot kTlsKey = ThreadLocals::Alloc();
+class ThreadMessageWindow;
+
+// A concrete implementation that uses HWNDs and PostMessage. There is a
+// single instance of this class. When InitThreadMessageQueue is called,
+// a ThreadMessageWindow, containing an HWND, is created to receive message
+// posted to the calling thread of control. A ThreadLocal is set to detect
+// thread termination and destroy the window. A reference to the window is
+// also placed into a global map for use by the Send method to lookup the
+// destination window by thread id.
+class CRThreadMessageQueue : public ThreadMessageQueue {
+ public:
+  CRThreadMessageQueue() : thread_map_(NULL) {}
+
+  virtual bool InitThreadMessageQueue();
+  virtual ThreadId GetCurrentThreadId();
+  virtual bool Send(ThreadId thread_handle,
+                    int message_type,
+                    MessageData *message);
+ private:
+  friend class ThreadMessageWindow;
+  void HandleThreadMessage(int message_type, MessageData *message_data);
+
+  typedef std::map<ThreadId, ThreadMessageWindow*> ThreadMap;
+  Mutex thread_map_mutex_;
+  ThreadMap *thread_map_;
+
+  struct TlsData {
+    TlsData(ThreadId id, ThreadMessageWindow *window)
+        : thread_id(id), message_window(window) {}
+    ThreadId thread_id;
+    scoped_ptr<ThreadMessageWindow> message_window;
+  };
+
+  static void ThreadEndHook(void* value);
+};
+
+// Owns the window that has the message queue for a thread, and manages the
+// transfer of messages across thread boundries.
+class ThreadMessageWindow : public CWindowImpl<ThreadMessageWindow> {
+ public:
+  static const DWORD WM_THREAD_MESSAGE = WM_USER + 1;
+  BEGIN_MSG_MAP(ThreadMessageWindow)
+    MESSAGE_HANDLER(WM_THREAD_MESSAGE , OnThreadMessage)
+  END_MSG_MAP()
+
+  ThreadMessageWindow(ThreadId thread_id) : thread_id_(thread_id) {
+    if (!Create(kMessageOnlyWindowParent,
+                NULL, NULL,
+                kMessageOnlyWindowStyle)) {
+      assert(false);
+    }
+  }
+
+  ~ThreadMessageWindow() {
+    if (IsWindow()) {
+      DestroyWindow();
+    }
+  }
+  static void AsyncCallback(void* instance);
+
+  LRESULT OnThreadMessage(UINT msg, WPARAM unused_wparam,
+                          LPARAM unused_lparam, BOOL &handled);
+  void PostThreadMessage(int message_tyoe, MessageData *message_data);
+  void ProcessThreadMessages();
+ private:
+  struct MessageEvent {
+    MessageEvent() {}
+    MessageEvent(int message_type, MessageData *message_data)
+        : message_type(message_type), message_data(message_data) {}
+    int message_type;
+    linked_ptr<MessageData> message_data;
+  };
+
+  ThreadId thread_id_;
+  Mutex events_mutex_;
+  std::deque<MessageEvent> events_;
+};
+
+
+static CRThreadMessageQueue g_instance;
+
+// static
+ThreadMessageQueue *ThreadMessageQueue::GetInstance() {
+  return &g_instance;
+}
+
+
+bool CRThreadMessageQueue::InitThreadMessageQueue() {
+  if (ThreadLocals::HasValue(kTlsKey)) {
+    return true;  // already initialized
+  }
+
+  ThreadId thread_id = GetCurrentThreadId();
+
+  // Note: We have to be careful about dead locks here and in
+  // ThreadEndHook below. ThreadEndHook is called via DllMain which
+  // is called by the loader with the loader lock being held. So
+  // we have to avoid calling anything that may acquire the loader
+  // lock while we have our mutex locked. This is why we create
+  // the window prior to locking our mutex.
+  TlsData *data = new TlsData(thread_id, new ThreadMessageWindow(thread_id));
+  ThreadLocals::SetValue(kTlsKey, data, &ThreadEndHook);
+
+  MutexLock lock(&thread_map_mutex_);
+  if (!thread_map_) {
+    thread_map_ = new ThreadMap;
+  }
+  (*thread_map_)[data->thread_id] = data->message_window.get();
+  return true;
+}
+
+
+// static
+void CRThreadMessageQueue::ThreadEndHook(void* value) {
+  TlsData *data = reinterpret_cast<TlsData*>(value);
+  if (data) {
+    // Scoped to release the lock prior to window deletion
+    {
+      MutexLock lock(&g_instance.thread_map_mutex_);
+      assert(g_instance.thread_map_);
+      g_instance.thread_map_->erase(data->thread_id);
+    }
+
+    delete data;
+  }
+}
+
+
+ThreadId CRThreadMessageQueue::GetCurrentThreadId() {
+  return ::GetCurrentThreadId();
+}
+
+bool CRThreadMessageQueue::Send(ThreadId thread_id,
+                                int message_type,
+                                MessageData *message_data) {
+  scoped_ptr<MessageData> scoped_message_data(message_data);
+  MutexLock lock(&thread_map_mutex_);
+  if (!thread_map_) {
+    return false;
+  }
+  ThreadMap::iterator found = thread_map_->find(thread_id);
+  if (found == thread_map_->end()) {
+    return false;
+  }
+  found->second->PostThreadMessage(message_type,
+                                   scoped_message_data.release());
+  return true;
+}
+
+void CRThreadMessageQueue::HandleThreadMessage(int message_type,
+                                               MessageData *message_data) {
+  RegisteredHandler handler;
+  if (GetRegisteredHandler(message_type, &handler)) {
+    handler.Invoke(message_type, message_data);
+  }
+}
+
+LRESULT ThreadMessageWindow::OnThreadMessage(UINT msg, WPARAM unused_wparam,
+                                             LPARAM unused_lparam,
+                                             BOOL &handled) {
+  assert(msg == WM_THREAD_MESSAGE);
+
+  ProcessThreadMessages();
+  handled = TRUE;
+  return 0;
+}
+
+void ThreadMessageWindow::AsyncCallback(void* instance) {
+  ThreadMessageWindow* window =
+      reinterpret_cast<ThreadMessageWindow *>(instance);
+  window->ProcessThreadMessages();
+}
+
+void ThreadMessageWindow::ProcessThreadMessages() {
+  // Swap contents of events queue into local variable.
+  std::deque<MessageEvent> local_events;
+  {
+    MutexLock lock(&events_mutex_);
+    events_.swap(local_events);
+  }
+  assert(!local_events.empty());
+
+  // Dispatch all pending messages.
+  while (!local_events.empty()) {
+    MessageEvent &event = local_events.front();
+    g_instance.HandleThreadMessage(event.message_type,
+                                   event.message_data.get());
+    local_events.pop_front();
+  }
+}
+
+void ThreadMessageWindow::PostThreadMessage(int message_type,
+                                            MessageData *message_data) {
+  MutexLock lock(&events_mutex_);
+  events_.push_back(MessageEvent(message_type, message_data));
+  if (events_.size() == 1) {
+    // If we're sending it to the main thread, there is no message window.
+    if (thread_id_ == CP::plugin_thread_id() &&
+        g_cpbrowser_funcs.plugin_thread_async_call) {
+      g_cpbrowser_funcs.plugin_thread_async_call(g_cpid, AsyncCallback, this);
+    } else {
+      PostMessage(WM_THREAD_MESSAGE, 0, 0);
+    }
+  }
+}
==== 
//depot/googleclient/gears/opensource/third_party/chrome/chrome_plugin_api.h#2 
- 
c:\src-gears3/googleclient/gears/opensource/third_party/chrome/chrome_plugin_api.h
 ====
# action=edit type=text
--- googleclient/gears/opensource/third_party/chrome/chrome_plugin_api.h        
2008-09-17 14:36:01.000000000 -0700
+++ googleclient/gears/opensource/third_party/chrome/chrome_plugin_api.h        
2008-09-17 15:21:42.000000000 -0700
@@ -59,7 +59,7 @@
 // The current version of the API, used by the 'version' field of CPPluginFuncs
 // and CPBrowserFuncs.
 #define CP_MAJOR_VERSION 0
-#define CP_MINOR_VERSION 7
+#define CP_MINOR_VERSION 8
 #define CP_VERSION       ((CP_MAJOR_VERSION << 8) | (CP_MINOR_VERSION))
 
 #define CP_GET_MAJOR_VERSION(version) ((version & 0xff00) >> 8)
@@ -412,6 +412,10 @@
                                                    uint32 data_len,
                                                    void **retval,
                                                    uint32 *retval_len);
+
+typedef CPError (STDCALL *CPB_PluginThreadAsyncCallFunc)(CPID id,
+                                                         void (*func)(void *),
+                                                         void *userData);
 
 // Informs the plugin of raw data having been sent from another process.
 typedef void (STDCALL *CPP_OnMessageFunc)(void *data, uint32 data_len);
@@ -491,6 +495,7 @@
   CPB_AddUICommandFunc add_ui_command;
   CPB_HandleCommandFunc handle_command;
   CPB_SendSyncMessageFunc send_sync_message;
+  CPB_PluginThreadAsyncCallFunc plugin_thread_async_call;
 } CPBrowserFuncs;
 
 

Reply via email to