/******************************************************************************* Copyright (c) 2017, Perforce Software, Inc. All rights reserved. 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. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 PERFORCE SOFTWARE, INC. 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. *******************************************************************************/ /******************************************************************************* * Name : P4Server.cs * * Author : dscheirer * * Description : A multithreaded manager for P4Server instances * ******************************************************************************/ using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Perforce.P4 { /// /// A multithreaded manager for P4Server. A P4Server instance should not be shared between threads; /// instead, either create a P4Server in each thread where you need a conenction, or a create a global P4ServerMT and call P4ServerMT.getThread() /// from the thread context to get a thread-safe connection to a Perforce server. /// public class P4ServerMT : IDisposable { private String server; private String user; private String pass; private String ws_client; private Dictionary mapTIDtoServer = new Dictionary(); private Object dictLock = new object(); private string cwd; private string trustFlag; private string fingerprint; /// /// Create a handler for later creation of P4Server using the provided parameters /// /// host:port for the P4 server /// User name for the login. Can be null/blank if only running commands that do not require a login. /// Password for the login. Can be null/blank if only running commands that do not require a login. /// Workspace (client) to be used by the connection. Can be null/blank if only running commands that do not require a login. /// public P4ServerMT(String server, String user, String pass, String ws_client) { this.server = server; this.user = user; this.pass = pass; this.ws_client = ws_client; } /// /// Create a handler for later creation of P4Server using the provided parameters /// /// Host:port for the P4 server. /// User name for the login. /// Can be null/blank if only running commands that do not require /// a login. /// Password for the login. Can be null/blank if /// only running commands that do not require a login. /// Workspace (client) to be used by the /// connection. Can be null/blank if only running commands that do /// not require a login. /// Current working directory /// Trust or not /// Fingerprint to trust public P4ServerMT(string server, string user, string pass, string ws_client, string cwd, string trust_flag, string fingerprint) : this(server, user, pass, ws_client) { this.cwd = cwd; this.trustFlag = trust_flag; this.fingerprint = fingerprint; } /// /// Create a handler for later creation of P4Server using the provided parameters /// /// Current working Directory. Can be null/blank if /// not connecting to the Perforce server using a P4Config file. public P4ServerMT(string cwd) { this.cwd = cwd; } public void Dispose() { // dispose of the P4Servers foreach (KeyValuePair entry in mapTIDtoServer) { entry.Value.Dispose(); } } /// /// Create/retreive a P4Server for use in this thread's context /// /// P4Server connection public P4Server getServer() { return getServerForThread(System.Threading.Thread.CurrentThread.ManagedThreadId); } /// /// Create/retreive a P4Server for a different thread /// /// Managed thread ID of the other thread /// P4Server connection public P4Server getServerForThread(int threadId) { lock (dictLock) { if (mapTIDtoServer.ContainsKey(threadId)) { return mapTIDtoServer[threadId]; } // only call the fingerprint constructor if we got configured with a fingerprint // otherwise it will not throw the correct "you need to trust this" exception // (which seems wrong, both methods should operate similarly)) P4Server p4server = (fingerprint != null && fingerprint.Length != 0) || (trustFlag != null && trustFlag.Length != 0) ? new P4Server(server, user, pass, ws_client, cwd, trustFlag, fingerprint) : new P4Server(server, user, pass, ws_client, cwd); p4server.SetThreadOwner(threadId); mapTIDtoServer[threadId] = p4server; return p4server; } } } }